aboutsummaryrefslogtreecommitdiff
path: root/nuttx/drivers
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2012-09-17 18:18:44 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2012-09-17 18:18:44 +0000
commit57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff (patch)
tree25d07d14e920d31c0b1947c9ca586f2a01fc32d8 /nuttx/drivers
downloadpx4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.tar.gz
px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.tar.bz2
px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.zip
Resync new repository with old repo r5166
git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5153 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/drivers')
-rw-r--r--nuttx/drivers/Kconfig414
-rw-r--r--nuttx/drivers/Makefile122
-rw-r--r--nuttx/drivers/README.txt145
-rw-r--r--nuttx/drivers/analog/Kconfig77
-rw-r--r--nuttx/drivers/analog/Make.defs87
-rw-r--r--nuttx/drivers/analog/ad5410.c205
-rw-r--r--nuttx/drivers/analog/adc.c423
-rw-r--r--nuttx/drivers/analog/ads1255.c335
-rw-r--r--nuttx/drivers/analog/dac.c499
-rw-r--r--nuttx/drivers/analog/pga11x.c793
-rw-r--r--nuttx/drivers/bch/Kconfig4
-rw-r--r--nuttx/drivers/bch/Make.defs52
-rw-r--r--nuttx/drivers/bch/bch_internal.h102
-rw-r--r--nuttx/drivers/bch/bchdev_driver.c255
-rw-r--r--nuttx/drivers/bch/bchdev_register.c104
-rw-r--r--nuttx/drivers/bch/bchdev_unregister.c160
-rw-r--r--nuttx/drivers/bch/bchlib_cache.c133
-rw-r--r--nuttx/drivers/bch/bchlib_read.c203
-rw-r--r--nuttx/drivers/bch/bchlib_sem.c84
-rw-r--r--nuttx/drivers/bch/bchlib_setup.c159
-rw-r--r--nuttx/drivers/bch/bchlib_teardown.c113
-rw-r--r--nuttx/drivers/bch/bchlib_write.c216
-rw-r--r--nuttx/drivers/can.c845
-rw-r--r--nuttx/drivers/dev_null.c135
-rw-r--r--nuttx/drivers/dev_zero.c135
-rw-r--r--nuttx/drivers/input/Kconfig13
-rw-r--r--nuttx/drivers/input/Make.defs72
-rw-r--r--nuttx/drivers/input/ads7843e.c1199
-rw-r--r--nuttx/drivers/input/ads7843e.h171
-rw-r--r--nuttx/drivers/input/stmpe811.h245
-rw-r--r--nuttx/drivers/input/stmpe811_adc.c266
-rw-r--r--nuttx/drivers/input/stmpe811_base.c546
-rw-r--r--nuttx/drivers/input/stmpe811_gpio.c454
-rw-r--r--nuttx/drivers/input/stmpe811_temp.c174
-rw-r--r--nuttx/drivers/input/stmpe811_tsc.c1144
-rw-r--r--nuttx/drivers/input/tsc2007.c1336
-rw-r--r--nuttx/drivers/input/tsc2007.h120
-rw-r--r--nuttx/drivers/lcd/Kconfig214
-rw-r--r--nuttx/drivers/lcd/Make.defs68
-rw-r--r--nuttx/drivers/lcd/README.txt165
-rw-r--r--nuttx/drivers/lcd/mio283qt2.c1014
-rw-r--r--nuttx/drivers/lcd/nokia6100.c1230
-rw-r--r--nuttx/drivers/lcd/p14201.c1246
-rw-r--r--nuttx/drivers/lcd/pcf8833.h152
-rw-r--r--nuttx/drivers/lcd/s1d15g10.h141
-rw-r--r--nuttx/drivers/lcd/sd1329.h527
-rw-r--r--nuttx/drivers/lcd/skeleton.c401
-rw-r--r--nuttx/drivers/lcd/ssd1289.c1279
-rw-r--r--nuttx/drivers/lcd/ssd1289.h425
-rw-r--r--nuttx/drivers/lcd/ssd1305.h211
-rw-r--r--nuttx/drivers/lcd/ug-9664hswag01.c1049
-rw-r--r--nuttx/drivers/loop.c509
-rw-r--r--nuttx/drivers/mmcsd/Kconfig77
-rw-r--r--nuttx/drivers/mmcsd/Make.defs46
-rw-r--r--nuttx/drivers/mmcsd/mmcsd_csd.h424
-rw-r--r--nuttx/drivers/mmcsd/mmcsd_debug.c183
-rw-r--r--nuttx/drivers/mmcsd/mmcsd_internal.h105
-rw-r--r--nuttx/drivers/mmcsd/mmcsd_sdio.c3180
-rw-r--r--nuttx/drivers/mmcsd/mmcsd_sdio.h339
-rw-r--r--nuttx/drivers/mmcsd/mmcsd_spi.c1878
-rw-r--r--nuttx/drivers/mmcsd/mmcsd_spi.h187
-rw-r--r--nuttx/drivers/mtd/Kconfig68
-rw-r--r--nuttx/drivers/mtd/Make.defs54
-rw-r--r--nuttx/drivers/mtd/at24xx.c429
-rw-r--r--nuttx/drivers/mtd/at45db.c899
-rw-r--r--nuttx/drivers/mtd/flash_eraseall.c117
-rw-r--r--nuttx/drivers/mtd/ftl.c578
-rw-r--r--nuttx/drivers/mtd/m25px.c798
-rw-r--r--nuttx/drivers/mtd/rammtd.c417
-rw-r--r--nuttx/drivers/mtd/ramtron.c674
-rw-r--r--nuttx/drivers/mtd/skeleton.c275
-rw-r--r--nuttx/drivers/mtd/sst25.c1250
-rw-r--r--nuttx/drivers/net/Kconfig79
-rw-r--r--nuttx/drivers/net/Make.defs71
-rw-r--r--nuttx/drivers/net/cs89x0.c959
-rw-r--r--nuttx/drivers/net/cs89x0.h326
-rw-r--r--nuttx/drivers/net/dm90x0.c1815
-rw-r--r--nuttx/drivers/net/e1000.c1049
-rw-r--r--nuttx/drivers/net/e1000.h123
-rw-r--r--nuttx/drivers/net/enc28j60.c2302
-rw-r--r--nuttx/drivers/net/enc28j60.h478
-rw-r--r--nuttx/drivers/net/skeleton.c692
-rw-r--r--nuttx/drivers/net/slip.c1017
-rw-r--r--nuttx/drivers/net/vnet.c673
-rw-r--r--nuttx/drivers/pipes/Kconfig4
-rw-r--r--nuttx/drivers/pipes/Make.defs46
-rw-r--r--nuttx/drivers/pipes/fifo.c139
-rw-r--r--nuttx/drivers/pipes/pipe.c286
-rw-r--r--nuttx/drivers/pipes/pipe_common.c682
-rw-r--r--nuttx/drivers/pipes/pipe_common.h139
-rw-r--r--nuttx/drivers/power/Kconfig23
-rw-r--r--nuttx/drivers/power/Make.defs84
-rw-r--r--nuttx/drivers/power/battery.c254
-rw-r--r--nuttx/drivers/power/max1704x.c564
-rw-r--r--nuttx/drivers/power/pm_activity.c166
-rw-r--r--nuttx/drivers/power/pm_changestate.c227
-rw-r--r--nuttx/drivers/power/pm_checkstate.c161
-rw-r--r--nuttx/drivers/power/pm_initialize.c112
-rw-r--r--nuttx/drivers/power/pm_internal.h210
-rw-r--r--nuttx/drivers/power/pm_register.c112
-rw-r--r--nuttx/drivers/power/pm_update.c334
-rw-r--r--nuttx/drivers/pwm.c676
-rw-r--r--nuttx/drivers/ramdisk.c342
-rw-r--r--nuttx/drivers/rwbuffer.c682
-rw-r--r--nuttx/drivers/sensors/Kconfig33
-rw-r--r--nuttx/drivers/sensors/Make.defs60
-rw-r--r--nuttx/drivers/sensors/lis331dl.c320
-rw-r--r--nuttx/drivers/sensors/lm75.c537
-rw-r--r--nuttx/drivers/sensors/qencoder.c402
-rw-r--r--nuttx/drivers/sercomm/Kconfig4
-rw-r--r--nuttx/drivers/sercomm/Make.defs55
-rw-r--r--nuttx/drivers/sercomm/README.txt19
-rw-r--r--nuttx/drivers/sercomm/console.c182
-rw-r--r--nuttx/drivers/sercomm/loadwriter.py19
-rw-r--r--nuttx/drivers/sercomm/uart.c469
-rw-r--r--nuttx/drivers/sercomm/uart.h32
-rw-r--r--nuttx/drivers/serial/Kconfig1049
-rw-r--r--nuttx/drivers/serial/Make.defs50
-rw-r--r--nuttx/drivers/serial/lowconsole.c132
-rw-r--r--nuttx/drivers/serial/serial.c954
-rw-r--r--nuttx/drivers/serial/serialirq.c186
-rw-r--r--nuttx/drivers/serial/uart_16550.c1164
-rw-r--r--nuttx/drivers/syslog/Kconfig73
-rw-r--r--nuttx/drivers/syslog/Make.defs68
-rw-r--r--nuttx/drivers/syslog/README.txt64
-rw-r--r--nuttx/drivers/syslog/ramlog.c770
-rw-r--r--nuttx/drivers/usbdev/Kconfig495
-rw-r--r--nuttx/drivers/usbdev/Make.defs63
-rw-r--r--nuttx/drivers/usbdev/cdcacm.c2212
-rw-r--r--nuttx/drivers/usbdev/cdcacm.h350
-rw-r--r--nuttx/drivers/usbdev/cdcacm_desc.c613
-rw-r--r--nuttx/drivers/usbdev/composite.c933
-rw-r--r--nuttx/drivers/usbdev/composite.h326
-rw-r--r--nuttx/drivers/usbdev/composite_desc.c291
-rw-r--r--nuttx/drivers/usbdev/pl2303.c2242
-rw-r--r--nuttx/drivers/usbdev/usbdev_trace.c233
-rw-r--r--nuttx/drivers/usbdev/usbdev_trprintf.c253
-rw-r--r--nuttx/drivers/usbdev/usbmsc.c1778
-rw-r--r--nuttx/drivers/usbdev/usbmsc.h694
-rw-r--r--nuttx/drivers/usbdev/usbmsc_desc.c421
-rw-r--r--nuttx/drivers/usbdev/usbmsc_scsi.c2667
-rw-r--r--nuttx/drivers/usbhost/Kconfig92
-rw-r--r--nuttx/drivers/usbhost/Make.defs57
-rw-r--r--nuttx/drivers/usbhost/hid_parser.c529
-rw-r--r--nuttx/drivers/usbhost/usbhost_enumerate.c518
-rw-r--r--nuttx/drivers/usbhost/usbhost_findclass.c199
-rw-r--r--nuttx/drivers/usbhost/usbhost_hidkbd.c2052
-rw-r--r--nuttx/drivers/usbhost/usbhost_registerclass.c117
-rw-r--r--nuttx/drivers/usbhost/usbhost_registry.c80
-rw-r--r--nuttx/drivers/usbhost/usbhost_registry.h87
-rw-r--r--nuttx/drivers/usbhost/usbhost_skeleton.c1060
-rw-r--r--nuttx/drivers/usbhost/usbhost_storage.c2244
-rw-r--r--nuttx/drivers/watchdog.c575
-rw-r--r--nuttx/drivers/wireless/Kconfig4
-rw-r--r--nuttx/drivers/wireless/Make.defs47
-rw-r--r--nuttx/drivers/wireless/cc1101/ISM1_868MHzGFSK100kbps.c113
-rw-r--r--nuttx/drivers/wireless/cc1101/ISM2_905MHzGFSK250kbps.c111
-rw-r--r--nuttx/drivers/wireless/cc1101/Kconfig4
-rw-r--r--nuttx/drivers/wireless/cc1101/cc1101.c812
159 files changed, 75890 insertions, 0 deletions
diff --git a/nuttx/drivers/Kconfig b/nuttx/drivers/Kconfig
new file mode 100644
index 000000000..294566d01
--- /dev/null
+++ b/nuttx/drivers/Kconfig
@@ -0,0 +1,414 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config DEV_NULL
+ bool "Enable /dev/null"
+ default y
+
+config DEV_ZERO
+ bool "Enable /dev/zero"
+ default n
+
+config LOOP
+ bool "Enable loop device"
+ default n
+ ---help---
+ Supports the standard loop device that can be used to export a
+ file (or character device) as a block device. See losetup() and
+ loteardown() in include/nuttx/fs/fs.h.
+
+config RAMDISK
+ bool "RAM Disk Support"
+ default n
+ ---help---
+ Can be used to set up a block of memory or (read-only) FLASH as
+ a block driver that can be mounted as a files system. See
+ include/nuttx/ramdisk.h.
+
+menuconfig CAN
+ bool "CAN Driver Support"
+ default n
+ ---help---
+ This selection enables building of the "upper-half" CAN driver.
+ See include/nuttx/can.h for further CAN driver information.
+
+if CAN
+config CAN_EXTID
+ bool "CAN extended IDs"
+ default n
+ ---help---
+ Enables support for the 29-bit extended ID. Default Standard 11-bit IDs.
+
+config CAN_FIFOSIZE
+ int "CAN driver I/O buffer size"
+ default 8
+ ---help---
+ The size of the circular buffer of CAN messages. Default: 8
+
+config CAN_NPENDINGRTR
+ int "Number of pending RTRs"
+ default 4
+ ---help---
+ The size of the list of pending RTR requests. Default: 4
+
+config CAN_LOOPBACK
+ bool "CAN extended IDs"
+ default n
+ ---help---
+ A CAN driver may or may not support a loopback mode for testing. If the
+ driver does support loopback mode, the setting will enable it. (If the
+ driver does not, this setting will have no effect).
+
+endif
+
+menuconfig PWM
+ bool "PWM Driver Support"
+ default n
+ ---help---
+ This selection enables building of the "upper-half" PWM driver.
+ See include/nuttx/pwm.h for further PWM driver information.
+
+if PWM
+config PWM_PULSECOUNT
+ bool "PWM Pulse Count Support"
+ default n
+ ---help---
+ Some hardware will support generation of a fixed number of pulses. This
+ might be used, for example to support a stepper motor. If the hardware
+ will support a fixed pulse count, then this configuration should be set to
+ enable the capability.
+
+endif
+
+menuconfig I2C
+ bool "I2C Driver Support"
+ default n
+ ---help---
+ This selection enables building of the "upper-half" I2C driver.
+ See include/nuttx/i2c.h for further I2C driver information.
+
+config I2C_SLAVE
+ bool "I2C Slave"
+ default n
+ depends on I2C
+
+config I2C_TRANSFER
+ bool "Support the I2C transfer() method"
+ default n
+ depends on I2C
+
+config I2C_WRITEREAD
+ bool "Support the I2C writeread() method"
+ default n
+ depends on I2C
+
+config I2C_POLLED
+ bool "Polled I2C (no interrupts)"
+ default n
+ depends on I2C
+
+config I2C_TRACE
+ bool "Enable I2C trace debug"
+ default n
+ depends on I2C
+
+config I2C_NTRACE
+ bool "Enable I2C trace debug"
+ default n
+ depends on I2C_TRACE
+
+menuconfig SPI
+ bool "SPI Driver Support"
+ default n
+ ---help---
+ This selection enables building of the "upper-half" SPI driver.
+ See include/nuttx/spi.h for further SPI driver information.
+
+if SPI
+config SPI_OWNBUS
+ bool "SPI single device"
+ default n
+ ---help---
+ Set if there is only one active device on the SPI bus. No locking or SPI
+ configuration will be performed. It is not necessary for clients to lock,
+ re-configure, etc..
+
+config SPI_EXCHANGE
+ bool "SPI exchange"
+ default y
+ ---help---
+ Driver supports a single exchange method (vs a recvblock() and sndblock ()methods).
+
+config SPI_CMDDATA
+ bool "SPI CMD/DATA"
+ default y
+ ---help---
+ Devices on the SPI bus require out-of-band support to distinguish command
+ transfers from data transfers. Such devices will often support either 9-bit
+ SPI (yech) or 8-bit SPI and a GPIO output that selects between command and data.
+
+endif
+
+menuconfig RTC
+ bool "RTC Driver Support"
+ default n
+ ---help---
+ This selection enables configuration of a real time clock (RTCdriver.
+ See include/nuttx/rtc.h for further watchdog timer driver information.
+ Most RTC drivers are MCU specific and may require other specific settings.
+
+config RTC_DATETIME
+ bool "Date/Time RTC Support"
+ default n
+ depends on RTC
+ ---help---
+ There are two general types of RTC: (1) A simple battery backed counter
+ that keeps the time when power is down, and (2) a full date / time RTC the
+ provides the date and time information, often in BCD format. If
+ RTC_DATETIME is selected, it specifies this second kind of RTC. In this
+ case, the RTC is used to "seed" the normal NuttX timer and the NuttX system
+ timer provides for higher resolution time.
+
+config RTC_HIRES
+ bool "Hi-Res RTC Support"
+ default n
+ depends on RTC && !RTC_DATETIME
+ ---help---
+ If RTC_DATETIME not selected, then the simple, battery backed counter is
+ used. There are two different implementations of such simple counters
+ based on the time resolution of the counter: The typical RTC keeps time
+ to resolution of 1 second, usually supporting a 32-bit time_t value. In
+ this case, the RTC is used to "seed" the normal NuttX timer and the NuttX
+ timer provides for higherresoution time.
+
+ If RTC_HIRES is enabled in the NuttX configuration, then the RTC provides
+ higher resolution time and completely replaces the system timer for purpose
+ of date and time.
+
+config RTC_FREQUENCY
+ int "Hi-Res RTC frequency"
+ default 1
+ depends on RTC && !RTC_DATETIME && RTC_HIRES
+ ---help---
+ If RTC_HIRES is defined, then the frequency of the high resolution RTC
+ must be provided. If RTC_HIRES is not defined, RTC_FREQUENCY is assumed
+ to be one Hz.
+
+config RTC_ALARM
+ bool "RTC Alarm Support"
+ default n
+ depends on RTC
+ ---help---
+ Enable if the RTC hardware supports setting of an alarm. A callback
+ function will be executed when the alarm goes off.
+
+menuconfig WATCHDOG
+ bool "Watchdog Timer Support"
+ default n
+ ---help---
+ This selection enables building of the "upper-half" watchdog timer driver.
+ See include/nuttx/watchdog.h for further watchdog timer driver information.
+
+if WATCHDOG
+endif
+
+menuconfig ANALOG
+ bool "Analog Device(ADC/DAC) Support"
+ default n
+ ---help---
+ This directory holds implementations of analog device drivers.
+ This includes drivers for Analog to Digital Conversion (ADC) as
+ well as drivers for Digital to Analog Conversion (DAC).
+ See include/nuttx/analog/*.h for registration information.
+
+if ANALOG
+source drivers/analog/Kconfig
+endif
+
+menuconfig BCH
+ bool "Block-to-Character (BCH) Support"
+ default n
+ ---help---
+ Contains logic that may be used to convert a block driver into
+ a character driver. This is the complementary conversion as that
+ performed by loop.c. See include/nuttx/fs/fs.h for registration
+ information.
+
+if BCH
+source drivers/bch/Kconfig
+endif
+
+menuconfig INPUT
+ bool "Input Device Support"
+ default n
+ ---help---
+ This directory holds implementations of input device drivers.
+ This includes such things as touchscreen and keypad drivers.
+ See include/nuttx/input/*.h for registration information.
+
+if INPUT
+source drivers/input/Kconfig
+endif
+
+menuconfig LCD
+ bool "LCD Driver Support"
+ default n
+ select NX_LCDDRIVER
+ ---help---
+ Drivers for parallel and serial LCD and OLED type devices. These
+ drivers support interfaces as defined in include/nuttx/lcd/lcd.h
+
+ This selection is necessary to enable support for LCD drivers in
+ drivers/lcd as well as for board-specific LCD drivers in the configs/
+ subdirectories.
+
+if LCD
+source drivers/lcd/Kconfig
+endif
+
+menuconfig MMCSD
+ bool "MMC/SD Driver Support"
+ default n
+ ---help---
+ Support for MMC/SD block drivers. MMC/SD block drivers based on
+ SPI and SDIO/MCI interfaces are supported. See include/nuttx/mmcsd.h
+ and include/nuttx/sdio.h for further information.
+
+if MMCSD
+source drivers/mmcsd/Kconfig
+endif
+
+menuconfig MTD
+ bool "Memory Technology Device (MTD) Support"
+ default n
+ ---help---
+ Memory Technology Device (MTD) drivers. Some simple drivers for
+ memory technologies like FLASH, EEPROM, NVRAM, etc. See
+ include/nuttx/mtd.h
+
+ (Note: This is a simple memory interface and should not be
+ confused with the "real" MTD developed at infradead.org. This
+ logic is unrelated; I just used the name MTD because I am not
+ aware of any other common way to refer to this class of devices).
+
+if MTD
+source drivers/mtd/Kconfig
+endif
+
+menuconfig NETDEVICES
+ bool "Network Device Support"
+ default n
+ depends on NET
+ ---help---
+ Network interface drivers. See also include/nuttx/net/net.h
+
+if NETDEVICES
+source drivers/net/Kconfig
+endif
+
+menuconfig PIPES
+ bool "FIFO and named pipe drivers"
+ default n
+ ---help---
+ FIFO and named pipe drivers. Standard interfaces are declared
+ in include/unistd.h
+
+if PIPES
+source drivers/pipes/Kconfig
+endif
+
+config PM
+ bool "Power management (PM) driver interfaces"
+ default n
+ ---help---
+ Power management (PM) driver interfaces. These interfaces are used
+ to manage power usage of a platform by monitoring driver activity
+ and by placing drivers into reduce power usage modes when the
+ drivers are not active.
+
+menuconfig POWER
+ bool "Power Management Support"
+ default n
+ ---help---
+ Enable building of power-related devices (battery monitors, chargers, etc).
+
+if POWER
+source drivers/power/Kconfig
+endif
+
+menuconfig SENSORS
+ bool "Sensor Device Support"
+ default n
+ ---help---
+ Drivers for various sensors
+
+if SENSORS
+source drivers/sensors/Kconfig
+endif
+
+menuconfig SERCOMM_CONSOLE
+ bool "Osmocom-bb Sercomm Driver Support"
+ default n
+ ---help---
+ Sercomm is the transport used by osmocom-bb that runs on top of serial.
+ See http://bb.osmocom.org/trac/wiki/nuttx-bb/run for detailed the usage
+ of nuttx with sercomm.
+
+ drivers/sercomm is only built if SERCOMM_CONSOLE in the NuttX
+ configuration file. If you attempt to build this driver without
+ osmocom-bb, you will get compilation errors because of header files
+ that are needed from the osmocom-bb.
+
+if SERCOMM
+source drivers/sercomm/Kconfig
+endif
+
+menuconfig SERIAL
+ bool "Serial Driver Support"
+ default y
+ ---help---
+ Front-end character drivers for chip-specific UARTs. This provide
+ some TTY-like functionality and are commonly used (but not required for)
+ the NuttX system console. See also include/nuttx/serial/serial.h
+
+if SERIAL
+source drivers/serial/Kconfig
+endif
+
+menuconfig USBDEV
+ bool "USB Device Driver Support"
+ default n
+ ---help---
+ USB device drivers. See also include/nuttx/usb/usbdev.h
+
+if USBDEV
+source drivers/usbdev/Kconfig
+endif
+
+menuconfig USBHOST
+ bool "USB Host Driver Support"
+ default n
+ ---help---
+ USB host drivers. See also include/nuttx/usb/usbhost.h
+
+if USBHOST
+source drivers/usbhost/Kconfig
+endif
+
+menuconfig WIRELESS
+ bool "Wireless Device Support"
+ default n
+ ---help---
+ Drivers for various wireless devices.
+
+if WIRELESS
+source drivers/wireless/Kconfig
+endif
+
+comment "System Logging Device Options"
+
+source drivers/syslog/Kconfig
+
+
diff --git a/nuttx/drivers/Makefile b/nuttx/drivers/Makefile
new file mode 100644
index 000000000..26a2ea992
--- /dev/null
+++ b/nuttx/drivers/Makefile
@@ -0,0 +1,122 @@
+############################################################################
+# drivers/Makefile
+#
+# Copyright (C) 2007-2012 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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 $(TOPDIR)/Make.defs
+
+ifeq ($(WINTOOL),y)
+INCDIROPT = -w
+endif
+
+DEPPATH = --dep-path .
+ASRCS =
+CSRCS =
+VPATH = .
+
+# Include support for various drivers. Each Make.defs file will add its
+# files to the source file list, add its DEPPATH info, and will add
+# the appropriate paths to the VPATH variable
+
+include analog/Make.defs
+include bch/Make.defs
+include input/Make.defs
+include lcd/Make.defs
+include mmcsd/Make.defs
+include mtd/Make.defs
+include net/Make.defs
+include pipes/Make.defs
+include power/Make.defs
+include sensors/Make.defs
+include sercomm/Make.defs
+include serial/Make.defs
+include syslog/Make.defs
+include usbdev/Make.defs
+include usbhost/Make.defs
+include wireless/Make.defs
+
+ifneq ($(CONFIG_NFILE_DESCRIPTORS),0)
+ CSRCS += dev_null.c dev_zero.c loop.c
+
+ifneq ($(CONFIG_DISABLE_MOUNTPOINT),y)
+ CSRCS += ramdisk.c rwbuffer.c
+endif
+
+ifeq ($(CONFIG_CAN),y)
+ CSRCS += can.c
+endif
+
+ifeq ($(CONFIG_PWM),y)
+ CSRCS += pwm.c
+endif
+
+ifeq ($(CONFIG_WATCHDOG),y)
+ CSRCS += watchdog.c
+endif
+endif
+
+AOBJS = $(ASRCS:.S=$(OBJEXT))
+COBJS = $(CSRCS:.c=$(OBJEXT))
+
+SRCS = $(ASRCS) $(CSRCS)
+OBJS = $(AOBJS) $(COBJS)
+
+BIN = libdrivers$(LIBEXT)
+
+all: $(BIN)
+
+$(AOBJS): %$(OBJEXT): %.S
+ $(call ASSEMBLE, $<, $@)
+
+$(COBJS): %$(OBJEXT): %.c
+ $(call COMPILE, $<, $@)
+
+$(BIN): $(OBJS)
+ @( for obj in $(OBJS) ; do \
+ $(call ARCHIVE, $@, $${obj}); \
+ done ; )
+
+.depend: Makefile $(SRCS)
+ @$(MKDEP) $(DEPPATH) $(CC) -- $(CFLAGS) -- $(SRCS) >Make.dep
+ @touch $@
+
+depend: .depend
+
+clean:
+ @rm -f $(BIN) *~ .*.swp
+ $(call CLEAN)
+
+distclean: clean
+ @rm -f Make.dep .depend
+
+-include Make.dep
diff --git a/nuttx/drivers/README.txt b/nuttx/drivers/README.txt
new file mode 100644
index 000000000..e27e8c583
--- /dev/null
+++ b/nuttx/drivers/README.txt
@@ -0,0 +1,145 @@
+README
+^^^^^^
+
+This directory contains various device drivers -- both block and
+character drivers as well as other more specialized drivers.
+
+Contents:
+ - Files in this directory
+ - Subdirectories of this directory
+ - Skeleton files
+
+Files in this directory
+^^^^^^^^^^^^^^^^^^^^^^^
+
+can.c
+ This is a CAN driver. See include/nuttx/can.h for usage information.
+
+dev_null.c and dev_zero.c
+ These files provide the standard /dev/null and /dev/zero devices.
+ See include/nuttx/fs/fs.h for functions that should be called if you
+ want to register these devices (devnull_register() and
+ devzero_register()).
+
+loop.c
+ Supports the standard loop device that can be used to export a
+ file (or character device) as a block device. See losetup() and
+ loteardown() in include/nuttx/fs/fs.h.
+
+pwm.c
+ Provides the "upper half" of a pulse width modulation (PWM) driver.
+ The "lower half" of the PWM driver is provided by device-specific
+ logic. See include/nuttx/pwm.h for usage information.
+
+ramdisk.c
+ Can be used to set up a block of memory or (read-only) FLASH as
+ a block driver that can be mounted as a files system. See
+ include/nuttx/ramdisk.h.
+
+ramlog.c
+ This is a driver that was intended to support debugging output,
+ aka syslogging, when the normal serial output is not available.
+ For example, if you are using a telnet or USB serial console,
+ the debug output will get lost.
+
+ This driver is similar to a pipe in that it saves the debugging
+ output in a FIFO in RAM. It differs from a pipe in numerous
+ details as needed to support logging.
+
+ This driver is built when CONFIG_RAMLOG is defined in the Nuttx
+ configuration.
+
+rwbuffer.c
+ A facility that can be use by any block driver in-order to add
+ writing buffering and read-ahead buffering.
+
+Subdirectories of this directory:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+analog/
+ This directory holds implementations of analog device drivers.
+ This includes drivers for Analog to Digital Conversion (ADC) as
+ well as drivers for Digital to Analog Conversion (DAC).
+ See include/nuttx/analog/*.h for registration information.
+
+bch/
+ Contains logic that may be used to convert a block driver into
+ a character driver. This is the complementary conversion as that
+ performed by loop.c. See include/nuttx/fs/fs.h for registration
+ information.
+
+input/
+ This directory holds implementations of input device drivers.
+ This includes such things as touchscreen and keypad drivers.
+ See include/nuttx/input/*.h for registration information.
+
+lcd/
+ Drivers for parallel and serial LCD and OLED type devices. These
+ drivers support interfaces as defined in include/nuttx/lcd/lcd.h
+
+mmcsd/
+ Support for MMC/SD block drivers. MMC/SD block drivers based on
+ SPI and SDIO/MCI interfaces are supported. See include/nuttx/mmcsd.h
+ and include/nuttx/sdio.h for further information.
+
+mtd/
+ Memory Technology Device (MTD) drivers. Some simple drivers for
+ memory technologies like FLASH, EEPROM, NVRAM, etc. See
+ include/nuttx/mtd.h
+
+ (Note: This is a simple memory interface and should not be
+ confused with the "real" MTD developed at infradead.org. This
+ logic is unrelated; I just used the name MTD because I am not
+ aware of any other common way to refer to this class of devices).
+
+net/
+ Network interface drivers. See also include/nuttx/net/net.h
+
+pipes/
+ FIFO and named pipe drivers. Standard interfaces are declared
+ in include/unistd.h
+
+power/
+ Power management (PM) driver interfaces. These interfaces are used
+ to manage power usage of a platform by monitoring driver activity
+ and by placing drivers into reduce power usage modes when the
+ drivers are not active.
+
+sensors/
+ Drivers for various sensors
+
+sercomm/
+ Sercomm is the transport used by osmocom-bb that runs on top of serial.
+ See http://bb.osmocom.org/trac/wiki/nuttx-bb/run for detailed the usage
+ of nuttx with sercomm.
+
+ drivers/sercomm is only built if CONFIG_SERCOMM_CONSOLE in the NuttX
+ configuration file. If you attempt to build this driver without
+ osmocom-bb, you will get compilation errors because of header files
+ that are needed from the osmocom-bb.
+
+serial/
+ Front-end character drivers for chip-specific UARTs. This provide
+ some TTY-like functionality and are commonly used (but not required for)
+ the NuttX system console. See also include/nuttx/serial/serial.h
+
+usbdev/
+ USB device drivers. See also include/nuttx/usb/usbdev.h
+
+usbhost/
+ USB host drivers. See also include/nuttx/usb/usbhost.h
+
+wireless/
+ Drivers for various wireless devices.
+
+Skeleton Files
+^^^^^^^^^^^^^^
+
+Skeleton files a "empty" frameworks for NuttX drivers. They are provided to
+give you a good starting point if you want to create a new NuttX driver.
+The following skeleton files are available:
+
+ drivers/lcd/skeleton.c -- Skeleton LCD driver
+ drivers/mtd/skeleton.c -- Skeleton memory technology device drivers
+ drivers/net/skeleton.c -- Skeleton network/Ethernet drivers
+ drivers/usbhost/usbhost_skeleton.c -- Skeleton USB host class driver
diff --git a/nuttx/drivers/analog/Kconfig b/nuttx/drivers/analog/Kconfig
new file mode 100644
index 000000000..ebed79c78
--- /dev/null
+++ b/nuttx/drivers/analog/Kconfig
@@ -0,0 +1,77 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config ADC
+ bool "Analog-to-Digital Conversion"
+ default n
+ ---help---
+ Select to enable support for analog input device support. This includes
+ not only Analog-to-Digital Converters (ADC) but also amplifiers and
+ analog multiplexers.
+
+config ADC_ADS125X
+ bool "TI ADS1255/ADS1256 support"
+ default n
+ depends on ADC
+ select SPI
+
+config ADS1255_FREQUENCY
+ int "ADS1255/ADS1256 SPI frequency"
+ default 1000000
+ depends on ADC_ADS125X
+
+config ADC_PGA11X
+ bool "TI PGA112/3/6/7 support"
+ default n
+ depends on ADC
+ select SPI
+ ---help---
+ Enables support for the PGA112, PGA113, PGA116, PGA117 Zerø-Drift
+ PROGRAMMABLE GAIN AMPLIFIER with MUX
+
+config PGA11X_SPIFREQUENCY
+ int "TI PGA112/3/6/7 SPI frequency"
+ default 1000000
+ depends on ADC_PGA11X
+ ---help---
+ PGA11x SPI frequency.
+
+config PGA11X_SPIMODE
+ int "TI PGA112/3/6/7 SPI mode"
+ default 0
+ depends on ADC_PGA11X
+ ---help---
+ PGA11x SPI mode. The specification says that the device operates in Mode 0 or
+ Mode 3. But sometimes you need to tinker with this to get things to work
+ correctly. Default: Mode 0
+
+config PGA11X_DAISYCHAIN
+ bool "TI PGA112/3/6/7 daisy chain mode"
+ default n
+ depends on ADC_PGA11X
+ ---help---
+ Enable support to use two PGA116/7's in Daisy Chain configuration.
+
+config PGA11X_MULTIPLE
+ bool "Multiple TI PGA112/3/6/7 support"
+ default n
+ depends on ADC_PGA11X && !PGA11X_DAISYCHAIN
+ ---help---
+ Can be defined to support multiple PGA11X devices on board with separate
+ chip selects (not daisy chained). Each device will require a customized
+ SPI interface to distinguish them when SPI_SELECT is called with
+ devid=SPIDEV_MUX.
+
+config DAC
+ bool "Digital-to-Analog Conversion"
+ default n
+ ---help---
+ Select to enable support for Digital-to-Analog Converters (DACs).
+
+config DAC_AD5410
+ bool "AD5410 support"
+ default n
+ depends on DAC
+ select SPI
diff --git a/nuttx/drivers/analog/Make.defs b/nuttx/drivers/analog/Make.defs
new file mode 100644
index 000000000..d94e39758
--- /dev/null
+++ b/nuttx/drivers/analog/Make.defs
@@ -0,0 +1,87 @@
+############################################################################
+# drivers/analog/Make.defs
+#
+# Copyright (C) 2011 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+# Don't build anything if there is no support for analog devices
+
+# Check for DAC devices
+
+ifeq ($(CONFIG_DAC),y)
+
+# Include the common DAC character driver
+
+CSRCS += dac.c
+
+# Include DAC device drivers
+
+ifeq ($(CONFIG_DAC_AD5410),y)
+ CSRCS += ad5410.c
+endif
+endif
+
+# Check for ADC devices
+
+ifeq ($(CONFIG_ADC),y)
+
+# Include the common ADC character driver
+
+CSRCS += adc.c
+
+# Amplifiers/multiplexers
+
+ifeq ($(CONFIG_ADC_PGA11X),y)
+ CSRCS += pga11x.c
+endif
+
+# Include ADC device drivers
+
+ifeq ($(CONFIG_ADC_ADS125X),y)
+ CSRCS += ads1255.c
+endif
+endif
+
+# Add analog driver build support (the nested if-then-else implements an OR)
+
+ifeq ($(CONFIG_DAC),y)
+ DEPPATH += --dep-path analog
+ VPATH += :analog
+ CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/analog}
+else
+ifeq ($(CONFIG_ADC),y)
+ DEPPATH += --dep-path analog
+ VPATH += :analog
+ CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/analog}
+endif
+endif
+
diff --git a/nuttx/drivers/analog/ad5410.c b/nuttx/drivers/analog/ad5410.c
new file mode 100644
index 000000000..3e925a3a9
--- /dev/null
+++ b/nuttx/drivers/analog/ad5410.c
@@ -0,0 +1,205 @@
+/************************************************************************************
+ * arch/drivers/analog/ad5410.c
+ *
+ * Copyright (C) 2011 Li Zhuoyi. All rights reserved.
+ * Author: Li Zhuoyi <lzyy.cn@gmail.com>
+ * History: 0.1 2011-08-05 initial version
+ *
+ * This file is a part of NuttX:
+ *
+ * Copyright (C) 2010 Gregory Nutt. 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <nuttx/config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/arch.h>
+#include <nuttx/analog/dac.h>
+#include <nuttx/spi.h>
+
+#if defined(CONFIG_DAC_AD5410)
+
+#define AD5410_REG_NOP 0x00
+#define AD5410_REG_WR 0x01
+#define AD5410_REG_RD 0x02
+#define AD5410_REG_CMD 0x55
+#define AD5410_REG_RST 0x56
+
+#define AD5410_CMD_REXT (1<<13)
+#define AD5410_CMD_OUTEN (1<<12)
+#define AD5410_CMD_SRCLK(x) (x<<8)
+#define AD5410_CMD_SRSTEP(x) (x<<5)
+#define AD5410_CMD_SREN (1<<4)
+#define AD5410_CMD_DCEN (1<<3)
+#define AD5410_CMD_420MA 0x05
+#define AD5410_CMD_020MA 0x06
+#define AD5410_CMD_024MA 0x07
+
+/****************************************************************************
+ * ad_private Types
+ ****************************************************************************/
+struct up_dev_s
+{
+ int devno;
+ FAR struct spi_dev_s *spi; /* Cached SPI device reference */
+};
+
+/****************************************************************************
+ * ad_private Function Prototypes
+ ****************************************************************************/
+
+/* DAC methods */
+
+static void dac_reset(FAR struct dac_dev_s *dev);
+static int dac_setup(FAR struct dac_dev_s *dev);
+static void dac_shutdown(FAR struct dac_dev_s *dev);
+static void dac_txint(FAR struct dac_dev_s *dev, bool enable);
+static int dac_send(FAR struct dac_dev_s *dev, FAR struct dac_msg_s *msg);
+static int dac_ioctl(FAR struct dac_dev_s *dev, int cmd, unsigned long arg);
+static int dac_interrupt(int irq, void *context);
+
+/****************************************************************************
+ * ad_private Data
+ ****************************************************************************/
+
+static const struct dac_ops_s g_dacops =
+{
+ .ao_reset =dac_reset,
+ .ao_setup = dac_setup,
+ .ao_shutdown = dac_shutdown,
+ .ao_txint = dac_txint,
+ .ao_send = dac_send,
+ .ao_ioctl = dac_ioctl,
+};
+
+static struct up_dev_s g_dacpriv;
+static struct dac_dev_s g_dacdev =
+{
+ .ad_ops = &g_dacops,
+ .ad_priv= &g_dacpriv,
+};
+
+/****************************************************************************
+ * ad_private Functions
+ ****************************************************************************/
+
+/* Reset the DAC device. Called early to initialize the hardware. This
+* is called, before ao_setup() and on error conditions.
+*/
+static void dac_reset(FAR struct dac_dev_s *dev)
+{
+
+}
+
+/* Configure the DAC. This method is called the first time that the DAC
+* device is opened. This will occur when the port is first opened.
+* This setup includes configuring and attaching DAC interrupts. Interrupts
+* are all disabled upon return.
+*/
+static int dac_setup(FAR struct dac_dev_s *dev)
+{
+ FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv;
+ FAR struct spi_dev_s *spi = priv->spi;
+ SPI_SELECT(spi, priv->devno, true);
+ SPI_SEND(spi,AD5410_REG_CMD);
+ SPI_SEND(spi,(AD5410_CMD_OUTEN|AD5410_CMD_420MA)>>8);
+ SPI_SEND(spi,AD5410_CMD_OUTEN|AD5410_CMD_420MA);
+ SPI_SELECT(spi, priv->devno, false);
+ return OK;
+}
+
+/* Disable the DAC. This method is called when the DAC device is closed.
+* This method reverses the operation the setup method.
+*/
+static void dac_shutdown(FAR struct dac_dev_s *dev)
+{
+}
+
+/* Call to enable or disable TX interrupts */
+static void dac_txint(FAR struct dac_dev_s *dev, bool enable)
+{
+}
+
+static int dac_send(FAR struct dac_dev_s *dev, FAR struct dac_msg_s *msg)
+{
+ FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv;
+ FAR struct spi_dev_s *spi = priv->spi;
+ SPI_SELECT(spi, priv->devno, true);
+ SPI_SEND(spi,AD5410_REG_WR);
+ SPI_SEND(spi,(uint8_t)(msg->am_data>>24));
+ SPI_SEND(spi,(uint8_t)(msg->am_data>>16));
+ SPI_SELECT(spi, priv->devno, false);
+ dac_txdone(&g_dacdev);
+ return 0;
+}
+
+/* All ioctl calls will be routed through this method */
+static int dac_ioctl(FAR struct dac_dev_s *dev, int cmd, unsigned long arg)
+{
+ dbg("Fix me:Not Implemented\n");
+ return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_ad5410cinitialize
+ *
+ * Description:
+ * Initialize the selected DAC port
+ *
+ * Input Parameter:
+ * Port number (for hardware that has mutiple DAC interfaces)
+ *
+ * Returned Value:
+ * Valid ad5410 device structure reference on succcess; a NULL on failure
+ *
+ ****************************************************************************/
+
+FAR struct dac_dev_s *up_ad5410initialize(FAR struct spi_dev_s *spi, unsigned int devno)
+{
+ FAR struct up_dev_s *priv = (FAR struct up_dev_s *)g_dacdev.ad_priv;
+ priv->spi=spi;
+ priv->devno=devno;
+ return &g_dacdev;
+}
+#endif
+
diff --git a/nuttx/drivers/analog/adc.c b/nuttx/drivers/analog/adc.c
new file mode 100644
index 000000000..84070f162
--- /dev/null
+++ b/nuttx/drivers/analog/adc.c
@@ -0,0 +1,423 @@
+/****************************************************************************
+ * drivers/analog/adc.c
+ *
+ * Copyright (C) 2011 Li Zhuoyi. All rights reserved.
+ * Author: Li Zhuoyi <lzyy.cn@gmail.com>
+ * History: 0.1 2011-08-04 initial version
+ *
+ * Derived from drivers/can.c
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/analog/adc.h>
+
+#include <arch/irq.h>
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int adc_open(FAR struct file *filep);
+static int adc_close(FAR struct file *filep);
+static ssize_t adc_read(FAR struct file *, FAR char *, size_t);
+static ssize_t adc_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+static int adc_ioctl(FAR struct file *filep,int cmd,unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations adc_fops =
+{
+ adc_open, /* open */
+ adc_close, /* close */
+ adc_read, /* read */
+ adc_write, /* write */
+ 0, /* seek */
+ adc_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , 0 /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+/************************************************************************************
+ * Name: adc_open
+ *
+ * Description:
+ * This function is called whenever the ADC device is opened.
+ *
+ ************************************************************************************/
+
+static int adc_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct adc_dev_s *dev = inode->i_private;
+ uint8_t tmp;
+ int ret = OK;
+
+ /* If the port is the middle of closing, wait until the close is finished */
+
+ if (sem_wait(&dev->ad_closesem) != OK)
+ {
+ ret = -errno;
+ }
+ else
+ {
+ /* Increment the count of references to the device. If this the first
+ * time that the driver has been opened for this device, then initialize
+ * the device.
+ */
+
+ tmp = dev->ad_ocount + 1;
+ if (tmp == 0)
+ {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ }
+ else
+ {
+ /* Check if this is the first time that the driver has been opened. */
+
+ if (tmp == 1)
+ {
+ /* Yes.. perform one time hardware initialization. */
+
+ irqstate_t flags = irqsave();
+ ret = dev->ad_ops->ao_setup(dev);
+ if (ret == OK)
+ {
+ /* Mark the FIFOs empty */
+
+ dev->ad_recv.af_head = 0;
+ dev->ad_recv.af_tail = 0;
+
+ /* Finally, Enable the CAN RX interrupt */
+
+ dev->ad_ops->ao_rxint(dev, true);
+
+ /* Save the new open count on success */
+
+ dev->ad_ocount = tmp;
+ }
+ irqrestore(flags);
+ }
+ }
+ sem_post(&dev->ad_closesem);
+ }
+ return ret;
+}
+
+/************************************************************************************
+ * Name: adc_close
+ *
+ * Description:
+ * This routine is called when the ADC device is closed.
+ * It waits for the last remaining data to be sent.
+ *
+ ************************************************************************************/
+
+static int adc_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct adc_dev_s *dev = inode->i_private;
+ irqstate_t flags;
+ int ret = OK;
+
+ if (sem_wait(&dev->ad_closesem) != OK)
+ {
+ ret = -errno;
+ }
+ else
+ {
+ /* Decrement the references to the driver. If the reference count will
+ * decrement to 0, then uninitialize the driver.
+ */
+
+ if (dev->ad_ocount > 1)
+ {
+ dev->ad_ocount--;
+ sem_post(&dev->ad_closesem);
+ }
+ else
+ {
+ /* There are no more references to the port */
+
+ dev->ad_ocount = 0;
+
+ /* Free the IRQ and disable the ADC device */
+
+ flags = irqsave(); /* Disable interrupts */
+ dev->ad_ops->ao_shutdown(dev); /* Disable the ADC */
+ irqrestore(flags);
+
+ sem_post(&dev->ad_closesem);
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: adc_read
+ ****************************************************************************/
+
+static ssize_t adc_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct adc_dev_s *dev = inode->i_private;
+ size_t nread;
+ irqstate_t flags;
+ int ret = 0;
+ int msglen;
+
+ avdbg("buflen: %d\n", (int)buflen);
+
+ if (buflen % 5 == 0)
+ msglen = 5;
+ else if (buflen % 4 == 0)
+ msglen = 4;
+ else if (buflen % 3 == 0)
+ msglen = 3;
+ else if (buflen % 2 == 0)
+ msglen = 2;
+ else if (buflen == 1)
+ msglen = 1;
+ else
+ msglen = 5;
+
+ if (buflen >= msglen)
+ {
+ /* Interrupts must be disabled while accessing the ad_recv FIFO */
+
+ flags = irqsave();
+ while (dev->ad_recv.af_head == dev->ad_recv.af_tail)
+ {
+ /* The receive FIFO is empty -- was non-blocking mode selected? */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ ret = -EAGAIN;
+ goto return_with_irqdisabled;
+ }
+
+ /* Wait for a message to be received */
+
+ dev->ad_nrxwaiters++;
+ ret = sem_wait(&dev->ad_recv.af_sem);
+ dev->ad_nrxwaiters--;
+ if (ret < 0)
+ {
+ ret = -errno;
+ goto return_with_irqdisabled;
+ }
+ }
+
+ /* The ad_recv FIFO is not empty. Copy all buffered data that will fit
+ * in the user buffer.
+ */
+
+ nread = 0;
+ do
+ {
+ FAR struct adc_msg_s *msg = &dev->ad_recv.af_buffer[dev->ad_recv.af_head];
+
+ /* Will the next message in the FIFO fit into the user buffer? */
+
+ if (nread + msglen > buflen)
+ {
+ /* No.. break out of the loop now with nread equal to the actual
+ * number of bytes transferred.
+ */
+
+ break;
+ }
+
+ /* Copy the message to the user buffer */
+
+ if (msglen == 1)
+ {
+ /* Only one channel,read highest 8-bits */
+
+ buffer[nread] = msg->am_data >> 24;
+ }
+ else if (msglen == 2)
+ {
+ /* Only one channel, read highest 16-bits */
+
+ *(int16_t *)&buffer[nread] = msg->am_data >> 16;
+ }
+ else if (msglen == 3)
+ {
+ /* Read channel highest 16-bits */
+
+ buffer[nread] = msg->am_channel;
+ *(int16_t *)&buffer[nread + 1] = msg->am_data >> 16;
+ }
+ else if (msglen == 4)
+ {
+ /* read channel highest 24-bits */
+
+ *(int32_t *)&buffer[nread] = msg->am_data;
+ buffer[nread] = msg->am_channel;
+ }
+ else
+ {
+ /* Read all */
+
+ *(int32_t *)&buffer[nread + 1] = msg->am_data;
+ buffer[nread] = msg->am_channel;
+ }
+ nread += msglen;
+
+ /* Increment the head of the circular message buffer */
+
+ if (++dev->ad_recv.af_head >= CONFIG_ADC_FIFOSIZE)
+ {
+ dev->ad_recv.af_head = 0;
+ }
+ }
+ while (dev->ad_recv.af_head != dev->ad_recv.af_tail);
+
+ /* All on the messages have bee transferred. Return the number of bytes
+ * that were read.
+ */
+
+ ret = nread;
+
+return_with_irqdisabled:
+ irqrestore(flags);
+ }
+
+ avdbg("Returning: %d\n", ret);
+ return ret;
+}
+
+/************************************************************************************
+ * Name: adc_write
+ ************************************************************************************/
+
+static ssize_t adc_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+{
+ return 0;
+}
+
+/************************************************************************************
+ * Name: adc_ioctl
+ ************************************************************************************/
+
+static int adc_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct adc_dev_s *dev = inode->i_private;
+ int ret = OK;
+
+ ret = dev->ad_ops->ao_ioctl(dev, cmd, arg);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int adc_receive(FAR struct adc_dev_s *dev, uint8_t ch, int32_t data)
+{
+ FAR struct adc_fifo_s *fifo = &dev->ad_recv;
+ int nexttail;
+ int err = -ENOMEM;
+
+ /* Check if adding this new message would over-run the drivers ability to enqueue
+ * read data.
+ */
+
+ nexttail = fifo->af_tail + 1;
+ if (nexttail >= CONFIG_ADC_FIFOSIZE)
+ {
+ nexttail = 0;
+ }
+
+ /* Refuse the new data if the FIFO is full */
+
+ if (nexttail != fifo->af_head)
+ {
+ /* Add the new, decoded CAN message at the tail of the FIFO */
+
+ fifo->af_buffer[fifo->af_tail].am_channel = ch;
+ fifo->af_buffer[fifo->af_tail].am_data = data;
+
+ /* Increment the tail of the circular buffer */
+
+ fifo->af_tail = nexttail;
+
+ if (dev->ad_nrxwaiters > 0)
+ {
+ sem_post(&fifo->af_sem);
+ }
+ err = OK;
+ }
+ return err;
+}
+
+int adc_register(FAR const char *path, FAR struct adc_dev_s *dev)
+{
+ /* Initialize the ADC device structure */
+
+ dev->ad_ocount = 0;
+
+ sem_init(&dev->ad_recv.af_sem, 0, 0);
+ sem_init(&dev->ad_closesem, 0, 1);
+
+ dev->ad_ops->ao_reset(dev);
+
+ return register_driver(path, &adc_fops, 0444, dev);
+}
diff --git a/nuttx/drivers/analog/ads1255.c b/nuttx/drivers/analog/ads1255.c
new file mode 100644
index 000000000..374decc54
--- /dev/null
+++ b/nuttx/drivers/analog/ads1255.c
@@ -0,0 +1,335 @@
+/************************************************************************************
+ * arch/drivers/analog/ads1255.c
+ *
+ * Copyright (C) 2011 Li Zhuoyi. All rights reserved.
+ * Author: Li Zhuoyi <lzyy.cn@gmail.com>
+ * History: 0.1 2011-08-05 initial version
+ * 0.2 2011-08-25 fix bug in g_adcdev (cd_ops -> ad_ops,cd_priv -> ad_priv)
+ *
+ * This file is a part of NuttX:
+ *
+ * Copyright (C) 2010 Gregory Nutt. 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <nuttx/config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/arch.h>
+#include <nuttx/spi.h>
+#include <nuttx/analog/adc.h>
+
+#if defined(CONFIG_ADC_ADS1255)
+
+#define ADS125X_BUFON 0x02
+#define ADS125X_BUFOFF 0x00
+
+#define ADS125X_PGA1 0x00
+#define ADS125X_PGA2 0x01
+#define ADS125X_PGA4 0x02
+#define ADS125X_PGA8 0x03
+#define ADS125X_PGA16 0x04
+#define ADS125X_PGA32 0x05
+#define ADS125X_PGA64 0x06
+
+#define ADS125X_RDATA 0x01 //Read Data
+#define ADS125X_RDATAC 0x03 //Read Data Continuously
+#define ADS125X_SDATAC 0x0F //Stop Read Data Continuously
+#define ADS125X_RREG 0x10 //Read from REG
+#define ADS125X_WREG 0x50 //Write to REG
+#define ADS125X_SELFCAL 0xF0 //Offset and Gain Self-Calibration
+#define ADS125X_SELFOCAL 0xF1 //Offset Self-Calibration
+#define ADS125X_SELFGCAL 0xF2 //Gain Self-Calibration
+#define ADS125X_SYSOCAL 0xF3 //System Offset Calibration
+#define ADS125X_SYSGCAL 0xF4 //System Gain Calibration
+#define ADS125X_SYNC 0xFC //Synchronize the A/D Conversion
+#define ADS125X_STANDBY 0xFD //Begin Standby Mode
+#define ADS125X_RESET 0xFE //Reset to Power-Up Values
+#define ADS125X_WAKEUP 0xFF //Completes SYNC and Exits Standby Mode
+
+#ifndef CONFIG_ADS1255_FREQUENCY
+#define CONFIG_ADS1255_FREQUENCY 1000000
+#endif
+#ifndef CONFIG_ADS1255_MUX
+#define CONFIG_ADS1255_MUX 0x01
+#endif
+#ifndef CONFIG_ADS1255_CHMODE
+#define CONFIG_ADS1255_CHMODE 0x00
+#endif
+#ifndef CONFIG_ADS1255_BUFON
+#define CONFIG_ADS1255_BUFON 1
+#endif
+#ifndef CONFIG_ADS1255_PGA
+#define CONFIG_ADS1255_PGA ADS125X_PGA2
+#endif
+#ifndef CONFIG_ADS1255_SPS
+#define CONFIG_ADS1255_SPS 50
+#endif
+
+/****************************************************************************
+ * ad_private Types
+ ****************************************************************************/
+
+struct up_dev_s
+{
+ uint8_t channel;
+ uint32_t sps;
+ uint8_t pga;
+ uint8_t buf;
+ const uint8_t *mux;
+ int irq;
+ int devno;
+ FAR struct spi_dev_s *spi; /* Cached SPI device reference */
+};
+
+/****************************************************************************
+ * ad_private Function Prototypes
+ ****************************************************************************/
+
+/* ADC methods */
+
+static void adc_reset(FAR struct adc_dev_s *dev);
+static int adc_setup(FAR struct adc_dev_s *dev);
+static void adc_shutdown(FAR struct adc_dev_s *dev);
+static void adc_rxint(FAR struct adc_dev_s *dev, bool enable);
+static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg);
+static int adc_interrupt(int irq, void *context);
+
+/****************************************************************************
+ * ad_private Data
+ ****************************************************************************/
+
+static const struct adc_ops_s g_adcops =
+{
+ .ao_reset = adc_reset, /* ao_reset */
+ .ao_setup = adc_setup, /* ao_setup */
+ .ao_shutdown = adc_shutdown, /* ao_shutdown */
+ .ao_rxint = adc_rxint, /* ao_rxint */
+ .ao_ioctl = adc_ioctl /* ao_read */
+};
+
+static struct up_dev_s g_adcpriv =
+{
+ .mux = (const uint8_t [])
+ {
+ CONFIG_ADS1255_MUX,0
+ },
+ .sps = CONFIG_ADS1255_SPS,
+ .channel = 0,
+ .irq = CONFIG_ADS1255_IRQ,
+};
+
+static struct adc_dev_s g_adcdev =
+{
+ .ad_ops = &g_adcops,
+ .ad_priv= &g_adcpriv,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint8_t getspsreg(uint16_t sps)
+{
+ static const unsigned short sps_tab[]=
+ {
+ 3,7,12,20,27,40,55,80,300,750,1500,3000,5000,10000,20000,65535,
+ };
+ static const unsigned char sps_reg[]=
+ {
+ 0x03,0x13,0x23,0x33,0x43,0x53,0x63,0x72,0x82,0x92,0xa1,0xb0,0xc0,0xd0,0xe0,0xf0,
+ };
+ int i;
+ for (i=0; i<16; i++)
+ {
+ if (sps<sps_tab[i])
+ break;
+ }
+ return sps_reg[i];
+}
+
+/****************************************************************************
+ * ad_private Functions
+ ****************************************************************************/
+/* Reset the ADC device. Called early to initialize the hardware. This
+* is called, before ao_setup() and on error conditions.
+*/
+
+static void adc_reset(FAR struct adc_dev_s *dev)
+{
+ FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv;
+ FAR struct spi_dev_s *spi = priv->spi;
+
+ SPI_SETMODE(spi, SPIDEV_MODE1);
+ SPI_SETBITS(spi, 8);
+ SPI_SETFREQUENCY(spi, CONFIG_ADS1255_FREQUENCY);
+ usleep(1000);
+ SPI_SELECT(spi, priv->devno, true);
+ SPI_SEND(spi,ADS125X_WREG+0x03); //WRITE SPS REG
+ SPI_SEND(spi,0x00); //count=1
+ SPI_SEND(spi,0x63);
+ SPI_SELECT(spi, priv->devno, false);
+}
+
+/* Configure the ADC. This method is called the first time that the ADC
+* device is opened. This will occur when the port is first opened.
+* This setup includes configuring and attaching ADC interrupts. Interrupts
+* are all disabled upon return.
+*/
+
+static int adc_setup(FAR struct adc_dev_s *dev)
+{
+ FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv;
+ FAR struct spi_dev_s *spi = priv->spi;
+ int ret = irq_attach(priv->irq, adc_interrupt);
+ if (ret == OK)
+ {
+ SPI_SELECT(spi, priv->devno, true);
+ SPI_SEND(spi,ADS125X_WREG); //WRITE REG from 0
+ SPI_SEND(spi,0x03); //count=4+1
+ if (priv->buf)
+ SPI_SEND(spi,ADS125X_BUFON); //REG0 STATUS BUFFER ON
+ else
+ SPI_SEND(spi,ADS125X_BUFOFF);
+ SPI_SEND(spi,priv->mux[0]);
+ SPI_SEND(spi,priv->pga); //REG2 ADCON PGA=2
+ SPI_SEND(spi,getspsreg(priv->sps));
+ usleep(1000);
+ SPI_SEND(spi,ADS125X_SELFCAL);
+ SPI_SELECT(spi, priv->devno, false);
+ up_enable_irq(priv->irq);
+ }
+ return ret;
+}
+
+/* Disable the ADC. This method is called when the ADC device is closed.
+* This method reverses the operation the setup method.
+*/
+
+static void adc_shutdown(FAR struct adc_dev_s *dev)
+{
+ FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv;
+ up_disable_irq(priv->irq);
+ irq_detach(priv->irq);
+}
+
+/* Call to enable or disable RX interrupts */
+
+static void adc_rxint(FAR struct adc_dev_s *dev, bool enable)
+{
+ FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->ad_priv;
+ if (enable)
+ up_enable_irq(priv->irq);
+ else
+ up_disable_irq(priv->irq);
+}
+
+/* All ioctl calls will be routed through this method */
+
+static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg)
+{
+ dbg("Fix me:Not Implemented\n");
+ return 0;
+}
+
+static int adc_interrupt(int irq, void *context)
+{
+ uint32_t regval;
+ FAR struct up_dev_s *priv = (FAR struct up_dev_s *)g_adcdev.ad_priv;
+ FAR struct spi_dev_s *spi = priv->spi;
+ unsigned char buf[4];
+ unsigned char ch;
+
+ SPI_SELECT(spi, priv->devno, true);
+ SPI_SEND(spi,ADS125X_RDATA);
+ up_udelay(10);
+ buf[3]=SPI_SEND(spi,0xff);
+ buf[2]=SPI_SEND(spi,0xff);
+ buf[1]=SPI_SEND(spi,0xff);
+ buf[0]=0;
+
+ priv->channel++;
+ ch = priv->mux[priv->channel];
+ if ( ch == 0 )
+ {
+ priv->channel=0;
+ ch = priv->mux[0];
+ }
+
+ SPI_SEND(spi,ADS125X_WREG+0x01);
+ SPI_SEND(spi,0x00);
+ SPI_SEND(spi,ch);
+ SPI_SEND(spi,ADS125X_SYNC);
+ up_udelay(2);
+ SPI_SEND(spi,ADS125X_WAKEUP);
+ SPI_SELECT(spi, priv->devno, false);
+
+ adc_receive(&g_adcdev,priv->channel,*(int32_t *)buf);
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_ads1255initialize
+ *
+ * Description:
+ * Initialize the selected adc port
+ *
+ * Input Parameter:
+ * Port number (for hardware that has mutiple adc interfaces)
+ *
+ * Returned Value:
+ * Valid can device structure reference on succcess; a NULL on failure
+ *
+ ****************************************************************************/
+
+FAR struct adc_dev_s *up_ads1255initialize(FAR struct spi_dev_s *spi, unsigned int devno)
+{
+ FAR struct up_dev_s *priv = (FAR struct up_dev_s *)g_adcdev.ad_priv;
+
+ /* Driver state data */
+
+ priv->spi = spi;
+ priv->devno = devno;
+ return &g_adcdev;
+}
+#endif
+
diff --git a/nuttx/drivers/analog/dac.c b/nuttx/drivers/analog/dac.c
new file mode 100644
index 000000000..e1fc3049f
--- /dev/null
+++ b/nuttx/drivers/analog/dac.c
@@ -0,0 +1,499 @@
+/****************************************************************************
+ * drivers/analog/dac.c
+ *
+ * Copyright (C) 2011 Li Zhuoyi. All rights reserved.
+ * Author: Li Zhuoyi <lzyy.cn@gmail.com>
+ * History: 0.1 2011-08-04 initial version
+ *
+ * Derived from drivers/can.c
+ *
+ * Copyright (C) 2008-2009Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/analog/dac.h>
+
+#include <arch/irq.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define HALF_SECOND_MSEC 500
+#define HALF_SECOND_USEC 500000L
+
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dac_open(FAR struct file *filep);
+static int dac_close(FAR struct file *filep);
+static ssize_t dac_read(FAR struct file *, FAR char *, size_t);
+static ssize_t dac_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+static int dac_ioctl(FAR struct file *filep,int cmd,unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations dac_fops =
+{
+ dac_open,
+ dac_close,
+ dac_read,
+ dac_write,
+ 0,
+ dac_ioctl
+#ifndef CONFIG_DISABLE_POLL
+ , 0
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+/************************************************************************************
+ * Name: dac_open
+ *
+ * Description:
+ * This function is called whenever the DAC device is opened.
+ *
+ ************************************************************************************/
+
+static int dac_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct dac_dev_s *dev = inode->i_private;
+ uint8_t tmp;
+ int ret = OK;
+
+ /* If the port is the middle of closing, wait until the close is finished */
+
+ if (sem_wait(&dev->ad_closesem) != OK)
+ {
+ ret = -errno;
+ }
+ else
+ {
+ /* Increment the count of references to the device. If this the first
+ * time that the driver has been opened for this device, then initialize
+ * the device.
+ */
+
+ tmp = dev->ad_ocount + 1;
+ if (tmp == 0)
+ {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ }
+ else
+ {
+ /* Check if this is the first time that the driver has been opened. */
+
+ if (tmp == 1)
+ {
+ /* Yes.. perform one time hardware initialization. */
+
+ irqstate_t flags = irqsave();
+ ret = dev->ad_ops->ao_setup(dev);
+ if (ret == OK)
+ {
+ /* Mark the FIFOs empty */
+
+ dev->ad_xmit.af_head = 0;
+ dev->ad_xmit.af_tail = 0;
+
+ /* Save the new open count on success */
+
+ dev->ad_ocount = tmp;
+ }
+ irqrestore(flags);
+ }
+ }
+ sem_post(&dev->ad_closesem);
+ }
+ return ret;
+}
+
+/************************************************************************************
+ * Name: dac_close
+ *
+ * Description:
+ * This routine is called when the DAC device is closed.
+ * It waits for the last remaining data to be sent.
+ *
+ ************************************************************************************/
+
+static int dac_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct dac_dev_s *dev = inode->i_private;
+ irqstate_t flags;
+ int ret = OK;
+
+ if (sem_wait(&dev->ad_closesem) != OK)
+ {
+ ret = -errno;
+ }
+ else
+ {
+ /* Decrement the references to the driver. If the reference count will
+ * decrement to 0, then uninitialize the driver.
+ */
+
+ if (dev->ad_ocount > 1)
+ {
+ dev->ad_ocount--;
+ sem_post(&dev->ad_closesem);
+ }
+ else
+ {
+ /* There are no more references to the port */
+
+ dev->ad_ocount = 0;
+
+ /* Now we wait for the transmit FIFO to clear */
+
+ while (dev->ad_xmit.af_head != dev->ad_xmit.af_tail)
+ {
+#ifndef CONFIG_DISABLE_SIGNALS
+ usleep(HALF_SECOND_USEC);
+#else
+ up_mdelay(HALF_SECOND_MSEC);
+#endif
+ }
+
+ /* Free the IRQ and disable the DAC device */
+
+ flags = irqsave(); /* Disable interrupts */
+ dev->ad_ops->ao_shutdown(dev); /* Disable the DAC */
+ irqrestore(flags);
+
+ sem_post(&dev->ad_closesem);
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: dac_read
+ ****************************************************************************/
+
+static ssize_t dac_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ return 0;
+}
+
+/************************************************************************************
+ * Name: dac_xmit
+ *
+ * Description:
+ * Send the message at the head of the ad_xmit FIFO
+ *
+ * Assumptions:
+ * Called with interrupts disabled
+ *
+ ************************************************************************************/
+
+static int dac_xmit(FAR struct dac_dev_s *dev)
+{
+ bool enable = false;
+ int ret = OK;
+
+ /* Check if the xmit FIFO is empty */
+
+ if (dev->ad_xmit.af_head != dev->ad_xmit.af_tail)
+ {
+ /* Send the next message at the head of the FIFO */
+
+ ret = dev->ad_ops->ao_send(dev, &dev->ad_xmit.af_buffer[dev->ad_xmit.af_head]);
+
+ /* Make sure the TX done interrupts are enabled */
+
+ enable = (ret == OK ? true : false);
+ }
+ dev->ad_ops->ao_txint(dev, enable);
+ return ret;
+}
+
+/************************************************************************************
+ * Name: dac_write
+ ************************************************************************************/
+
+static ssize_t dac_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct dac_dev_s *dev = inode->i_private;
+ FAR struct dac_fifo_s *fifo = &dev->ad_xmit;
+ FAR struct dac_msg_s *msg;
+ bool empty = false;
+ ssize_t nsent = 0;
+ irqstate_t flags;
+ int nexttail;
+ int msglen;
+ int ret = 0;
+
+ /* Interrupts must disabled throughout the following */
+
+ flags = irqsave();
+
+ /* Check if the TX FIFO was empty when we started. That is a clue that we have
+ * to kick off a new TX sequence.
+ */
+
+ empty = (fifo->af_head == fifo->af_tail);
+
+ /* Add the messages to the FIFO. Ignore any trailing messages that are
+ * shorter than the minimum.
+ */
+
+ if (buflen % 5 ==0 )
+ msglen=5;
+ else if (buflen % 4 ==0 )
+ msglen=4;
+ else if (buflen % 3 ==0 )
+ msglen=3;
+ else if (buflen % 2 ==0 )
+ msglen=2;
+ else if (buflen == 1)
+ msglen=1;
+ else
+ msglen=5;
+
+ while ((buflen - nsent) >= msglen )
+ {
+ /* Check if adding this new message would over-run the drivers ability to enqueue
+ * xmit data.
+ */
+
+ nexttail = fifo->af_tail + 1;
+ if (nexttail >= CONFIG_DAC_FIFOSIZE)
+ {
+ nexttail = 0;
+ }
+
+ /* If the XMIT fifo becomes full, then wait for space to become available */
+
+ while (nexttail == fifo->af_head)
+ {
+ /* The transmit FIFO is full -- was non-blocking mode selected? */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ if (nsent == 0)
+ {
+ ret = -EAGAIN;
+ }
+ else
+ {
+ ret = nsent;
+ }
+ goto return_with_irqdisabled;
+ }
+
+ /* If the FIFO was empty when we started, then we will have
+ * start the XMIT sequence to clear the FIFO.
+ */
+
+ if (empty)
+ {
+ dac_xmit(dev);
+ }
+
+ /* Wait for a message to be sent */
+
+ do
+ {
+ ret = sem_wait(&fifo->af_sem);
+ if (ret < 0 && errno != EINTR)
+ {
+ ret = -errno;
+ goto return_with_irqdisabled;
+ }
+ }
+ while (ret < 0);
+
+ /* Re-check the FIFO state */
+
+ empty = (fifo->af_head == fifo->af_tail);
+ }
+
+ /* We get here if there is space at the end of the FIFO. Add the new
+ * CAN message at the tail of the FIFO.
+ */
+
+ if (msglen==5)
+ {
+ msg = (FAR struct dac_msg_s *)&buffer[nsent];
+ memcpy(&fifo->af_buffer[fifo->af_tail], msg, msglen);
+ }
+ else if(msglen == 4)
+ {
+ fifo->af_buffer[fifo->af_tail].am_channel=buffer[nsent];
+ fifo->af_buffer[fifo->af_tail].am_data=*(uint32_t *)&buffer[nsent];
+ fifo->af_buffer[fifo->af_tail].am_data&=0xffffff00;
+ }
+ else if(msglen == 3)
+ {
+ fifo->af_buffer[fifo->af_tail].am_channel=buffer[nsent];
+ fifo->af_buffer[fifo->af_tail].am_data=(*(uint16_t *)&buffer[nsent+1]);
+ fifo->af_buffer[fifo->af_tail].am_data<<=16;
+ }
+ else if(msglen == 2)
+ {
+ fifo->af_buffer[fifo->af_tail].am_channel=0;
+ fifo->af_buffer[fifo->af_tail].am_data=(*(uint16_t *)&buffer[nsent]);
+ fifo->af_buffer[fifo->af_tail].am_data<<=16;
+ }
+ else if(msglen == 1)
+ {
+ fifo->af_buffer[fifo->af_tail].am_channel=0;
+ fifo->af_buffer[fifo->af_tail].am_data=buffer[nsent];
+ fifo->af_buffer[fifo->af_tail].am_data<<=24;
+ }
+ /* Increment the tail of the circular buffer */
+
+ fifo->af_tail = nexttail;
+
+ /* Increment the number of bytes that were sent */
+
+ nsent += msglen;
+ }
+
+ /* We get here after all messages have been added to the FIFO. Check if
+ * we need to kick of the XMIT sequence.
+ */
+
+ if (empty)
+ {
+ dac_xmit(dev);
+ }
+
+ /* Return the number of bytes that were sent */
+
+ ret = nsent;
+
+return_with_irqdisabled:
+ irqrestore(flags);
+ return ret;
+}
+
+/************************************************************************************
+ * Name: dac_ioctl
+ ************************************************************************************/
+
+static int dac_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct dac_dev_s *dev = inode->i_private;
+ int ret = OK;
+
+ ret = dev->ad_ops->ao_ioctl(dev, cmd, arg);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/************************************************************************************
+ * Name: dac_txdone
+ *
+ * Description:
+ * Called from the DAC interrupt handler at the completion of a send operation.
+ *
+ * Return:
+ * OK on success; a negated errno on failure.
+ *
+ ************************************************************************************/
+
+int dac_txdone(FAR struct dac_dev_s *dev)
+{
+ int ret = -ENOENT;
+
+ /* Verify that the xmit FIFO is not empty */
+
+ if (dev->ad_xmit.af_head != dev->ad_xmit.af_tail)
+ {
+ /* Remove the message at the head of the xmit FIFO */
+
+ if (++dev->ad_xmit.af_head >= CONFIG_DAC_FIFOSIZE)
+ {
+ dev->ad_xmit.af_head = 0;
+ }
+
+ /* Send the next message in the FIFO */
+
+ ret = dac_xmit(dev);
+ if (ret == OK)
+ {
+ /* Inform any waiting threads that new xmit space is available */
+
+ ret = sem_post(&dev->ad_xmit.af_sem);
+ }
+ }
+ return ret;
+}
+
+int dac_register(FAR const char *path, FAR struct dac_dev_s *dev)
+{
+ /* Initialize the DAC device structure */
+
+ dev->ad_ocount = 0;
+
+ sem_init(&dev->ad_xmit.af_sem, 0, 0);
+ sem_init(&dev->ad_closesem, 0, 1);
+
+ dev->ad_ops->ao_reset(dev);
+
+ return register_driver(path, &dac_fops, 0555, dev);
+}
+
diff --git a/nuttx/drivers/analog/pga11x.c b/nuttx/drivers/analog/pga11x.c
new file mode 100644
index 000000000..7bb4a11bb
--- /dev/null
+++ b/nuttx/drivers/analog/pga11x.c
@@ -0,0 +1,793 @@
+/****************************************************************************
+ * drivers/analog/pga11x.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "PGA112, PGA113, PGA116, PGA117: Zerø-Drift PROGRAMMABLE GAIN AMPLIFIER
+ * with MUX", SBOS424B, March 2008, Revised September 2008, Texas
+ * Instruments Incorporated"
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/analog/pga11x.h>
+
+#if defined(CONFIG_ADC) && defined(CONFIG_ADC_PGA11X)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* The PGA112/PGA113 have a three-wire SPI digital interface; the
+ * PGA116/PGA117 have a four-wire SPI digital interface. The PGA116/117 also
+ * have daisy-chain capability (The PGA112/PGA113 can be used as the last device
+ * in a daisy-chain as shown if write-only communication is acceptable).
+ */
+
+/* PGA11x commands (PGA112/PGA113) */
+
+#define PGA11X_CMD_READ 0x6a00
+#define PGA11X_CMD_WRITE 0x2a00
+#define PGA11X_CMD_NOOP 0x0000
+#define PGA11X_CMD_SDN_DIS 0xe100
+#define PGA11X_CMD_SDN_EN 0xe1f1
+
+/* SPI Daisy-Chain Commands (PGA116/PGA117) */
+
+#define PGA11X_DCCMD_SELECTOR 0x8000
+#define PGA11X_DCCMD_NOOP (PGA11X_DCCMD_SELECTOR | PGA11X_CMD_NOOP)
+#define PGA11X_DCCMD_SDN_DIS (PGA11X_DCCMD_SELECTOR | PGA11X_CMD_SDN_DIS)
+#define PGA11X_DCCMD_SDN_EN (PGA11X_DCCMD_SELECTOR | PGA11X_CMD_SDN_EN)
+#define PGA11X_DCCMD_READ (PGA11X_DCCMD_SELECTOR | PGA11X_CMD_READ)
+#define PGA11X_DCCMD_WRITE (PGA11X_DCCMD_SELECTOR | PGA11X_CMD_WRITE)
+
+/* Write command Gain Selection Bits (PGA112/PGA113)
+ *
+ * the PGA112 and PGA116 provide binary gain selections (1, 2, 4, 8, 16, 32,
+ * 64, 128); the PGA113 and PGA117 provide scope gain selections (1, 2, 5, 10,
+ * 20, 50, 100, 200).
+ */
+
+#define PGA11X_GAIN_SHIFT (4) /* Bits 4-7: Gain Selection Bits */
+#define PGA11X_GAIN_MASK (15 << PGA11X_GAIN_SHIFT)
+
+/* Write command Mux Channel Selection Bits
+ *
+ * The PGA112/PGA113 have a two-channel input MUX; the PGA116/PGA117 have a
+ * 10-channel input MUX.
+ */
+
+#define PGA11X_CHAN_SHIFT (0) /* Bits 0-3: Channel Selection Bits */
+#define PGA11X_CHAN_MASK (15 << PGA11X_CHAN_SHIFT)
+
+/* Other definitions ********************************************************/
+
+#define SPI_DUMMY 0xff
+
+/* Debug ********************************************************************/
+/* Check if (non-standard) SPI debug is enabled */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_DEBUG_SPI
+#endif
+
+#ifdef CONFIG_DEBUG_SPI
+# define spidbg dbg
+# ifdef CONFIG_DEBUG_VERBOSE
+# define spivdbg dbg
+# else
+# define spivdbg(x...)
+# endif
+#else
+# define spidbg(x...)
+# define spivdbg(x...)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pga11x_configure
+ *
+ * Description:
+ * Configure the SPI bus as needed for the PGA11x device.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void pga11x_configure(FAR struct spi_dev_s *spi)
+{
+ spivdbg("MODE: %d BITS: 8 Frequency: %d\n",
+ CONFIG_PGA11X_SPIMODE, CONFIG_PGA11X_SPIFREQUENCY);
+
+ /* Call the setfrequency, setbits, and setmode methods to make sure that
+ * the SPI is properly configured for the device.
+ */
+
+ SPI_SETMODE(spi, CONFIG_PGA11X_SPIMODE);
+ SPI_SETBITS(spi, 8);
+ (void)SPI_SETFREQUENCY(spi, CONFIG_PGA11X_SPIFREQUENCY);
+}
+
+/****************************************************************************
+ * Name: pga11x_lock
+ *
+ * Description:
+ * Lock the SPI bus and configure it as needed for the PGA11x device.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SPI_OWNBUS
+static void pga11x_lock(FAR struct spi_dev_s *spi)
+{
+ spivdbg("Locking\n");
+
+ /* On SPI busses where there are multiple devices, it will be necessary to
+ * lock SPI to have exclusive access to the busses for a sequence of
+ * transfers. The bus should be locked before the chip is selected.
+ *
+ * This is a blocking call and will not return until we have exclusiv access to
+ * the SPI buss. We will retain that exclusive access until the bus is unlocked.
+ */
+
+ SPI_LOCK(spi, true);
+
+ /* After locking the SPI bus, the we also need call the setfrequency, setbits, and
+ * setmode methods to make sure that the SPI is properly configured for the device.
+ * If the SPI buss is being shared, then it may have been left in an incompatible
+ * state.
+ */
+
+ pga11x_configure(spi);
+}
+#else
+# define pga11x_lock(spi)
+#endif
+
+/****************************************************************************
+ * Name: pga11x_unlock
+ *
+ * Description:
+ * Lock the SPI bus and configure it as needed for the PGA11x device.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SPI_OWNBUS
+static inline void pga11x_unlock(FAR struct spi_dev_s *spi)
+{
+ spivdbg("Unlocking\n");
+
+ SPI_LOCK(spi, false);
+}
+#else
+# define pga11x_unlock(spi)
+#endif
+
+/****************************************************************************
+ * Name: pga11x_send16
+ *
+ * Description:
+ * Send 16 bits of data, ignoring any returned data
+ *
+ * Input Parameters:
+ * spi - PGA11X driver instance
+ * word - The data to send
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The bus is locked and the device is selected
+ *
+ ****************************************************************************/
+
+static void pga11x_send16(FAR struct spi_dev_s *spi, uint16_t word)
+{
+ spivdbg("Send %04x\n", word);
+
+ /* The logical interface is 16-bits wide. However, this driver uses a
+ * 8-bit configuration for greaer portability.
+ *
+ * Send the MS byte first. Then the LS byte.
+ */
+
+ SPI_SEND(spi, word >> 8);
+ SPI_SEND(spi, word & 0xff);
+}
+
+/****************************************************************************
+ * Name: pga11x_recv16
+ *
+ * Description:
+ * Receive 16 bits of data.
+ *
+ * Input Parameters:
+ * spi - PGA11X driver instance
+ *
+ * Returned Value:
+ * The received 16-bit value
+ *
+ * Assumptions:
+ * The bus is locked and the device is selected
+ *
+ ****************************************************************************/
+
+static uint16_t pga11x_recv16(FAR struct spi_dev_s *spi)
+{
+ uint8_t msb;
+ uint8_t lsb;
+
+ /* The logical interface is 16-bits wide. However, this driver uses a
+ * 8-bit configuration for greaer portability.
+ *
+ * Send a dummy byte and receive MS byte first. Then the LS byte.
+ */
+
+ msb = SPI_SEND(spi, SPI_DUMMY);
+ lsb = SPI_SEND(spi, SPI_DUMMY);
+ spivdbg("Received %02x %02x\n", msb, lsb);
+
+ return ((uint16_t)msb << 8) | (uint16_t)lsb;
+}
+
+/****************************************************************************
+ * Name: pga11x_write
+ *
+ * Description:
+ * Send a 16-bit command.
+ *
+ * Input Parameters:
+ * spi - PGA11X driver instance
+ * cmd - PGA11X command (non-daisy chained)
+ * u1cmd - PGA11X U1 command (daisy chained)
+ * u2cmd - PGA11X U2 command (daisy chained)
+ *
+ * Returned Value:
+ * The received 16-bit value
+ *
+ * Assumptions:
+ * The device is NOT selected and the NOT bus is locked.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_PGA11X_DAISYCHAIN
+static void pga11x_write(FAR struct spi_dev_s *spi, uint16_t cmd)
+{
+ spivdbg("cmd %04x\n", cmd);
+
+ /* Lock, select, send the 16-bit command, de-select, and un-lock. */
+
+ pga11x_lock(spi);
+ SPI_SELECT(spi, SPIDEV_MUX, true);
+ pga11x_send16(spi, cmd);
+ SPI_SELECT(spi, SPIDEV_MUX, false);
+ pga11x_unlock(spi);
+}
+#else
+static void pga11x_write(FAR struct spi_dev_s *spi, uint16_t u1cmd, uint16_t u2cmd)
+{
+ spivdbg("U1 cmd: %04x U2 cmd: %04x\n", u1cmd, u2cmd);
+
+ /* Lock, select, send the U2 16-bit command, the U1 16-bit command, de-select,
+ * and un-lock.
+ */
+
+ pga11x_lock(spi);
+ SPI_SELECT(spi, SPIDEV_MUX, true);
+ pga11x_send16(spi, u2cmd);
+ pga11x_send16(spi, u1cmd);
+ SPI_SELECT(spi, SPIDEV_MUX, false);
+ pga11x_unlock(spi);
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pga11x_initialize
+ *
+ * Description:
+ * Initialize the PGA117 amplifier/multiplexer(s).
+ *
+ * Input Parameters:
+ * spi - An SPI "bottom half" device driver instance
+ *
+ * Returned Value:
+ * On success, a non-NULL opaque handle is returned; a NULL is returned
+ * on any failure. This handle may be used with the other PGA117 interface
+ * functions to control the multiplexer
+ *
+ ****************************************************************************/
+
+PGA11X_HANDLE pga11x_initialize(FAR struct spi_dev_s *spi)
+{
+ spivdbg("Entry\n");
+
+ /* Configure the SPI us for the device. Do this now only if the PGA11X is
+ * the only device on the bus.
+ */
+
+#ifdef CONFIG_SPI_OWNBUS
+ pga11x_configure(spi);
+#endif
+
+ /* No other special state is required, just return the SPI driver instance
+ * as the handle. This gives us a place to extend functionality in the
+ * future if neccessary.
+ */
+
+ return (PGA11X_HANDLE)spi;
+}
+
+/****************************************************************************
+ * Name: pga11x_select
+ *
+ * Description:
+ * Select an input channel and gain for all PGA11xs.
+ *
+ * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_select() configures
+ * both chips in the daisy-chain. pga11x_uselect() is provided to support
+ * configuring the parts in the daisychain independently.
+ *
+ * Input Parameters:
+ * spi - An SPI "bottom half" device driver instance
+ * settings - New channel and gain settings
+ *
+ * Returned Value:
+ * Zero on sucess; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int pga11x_select(PGA11X_HANDLE handle,
+ FAR const struct pga11x_settings_s *settings)
+{
+#ifndef CONFIG_PGA11X_DAISYCHAIN
+ FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle;
+ uint16_t cmd;
+
+ DEBUGASSERT(handle && settings);
+ spivdbg("channel: %d gain: %d\n", settings->channel, settings->gain);
+
+ /* Format the command */
+
+ cmd = PGA11X_CMD_WRITE |
+ ((uint16_t)settings->channel << PGA11X_CHAN_SHIFT) |
+ ((uint16_t)settings->gain << PGA11X_GAIN_SHIFT);
+
+ /* Send the command */
+
+ pga11x_write(spi, cmd);
+ return OK;
+#else
+ FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle;
+ uint16_t u1cmd;
+ uint16_t u2cmd;
+
+ DEBUGASSERT(handle && settings);
+ spivdbg("U1 channel: %d gain: %d\n", settings->u1.channel, settings->u1.gain);
+ spivdbg("U1 channel: %d gain: %d\n", settings->u1.channel, settings->u1.gain);
+
+ /* Format the commands */
+
+ u1cmd = PGA11X_CMD_WRITE |
+ ((uint16_t)settings->u1.channel << PGA11X_CHAN_SHIFT) |
+ ((uint16_t)settings->u1.gain << PGA11X_GAIN_SHIFT);
+
+ u2cmd = PGA11X_DCCMD_WRITE |
+ ((uint16_t)settings->u2.channel << PGA11X_CHAN_SHIFT) |
+ ((uint16_t)settings->u2.gain << PGA11X_GAIN_SHIFT);
+
+ /* Send the command */
+
+ pga11x_write(spi, u1cmd, u2cmd);
+ return OK;
+#endif
+}
+
+/****************************************************************************
+ * Name: pga11x_uselect
+ *
+ * Description:
+ * Select an input channel and gain for one PGA11x.
+ *
+ * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_uselect() configures
+ * one chips in the daisy-chain.
+ *
+ * Input Parameters:
+ * spi - An SPI "bottom half" device driver instance
+ * pos - Position of the chip in the daisy chain (0 or 1)
+ * settings - New channel and gain settings
+ *
+ * Returned Value:
+ * Zero on sucess; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PGA11X_DAISYCHAIN
+int pga11x_uselect(PGA11X_HANDLE handle, int pos,
+ FAR const struct pga11x_usettings_s *settings)
+{
+ FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle;
+ uint16_t u1cmd;
+ uint16_t u2cmd;
+
+ spivdbg("channel: %d gain: %d\n", settings->channel, settings->gain);
+ DEBUGASSERT(handle);
+
+ /* Format the commands */
+
+ if (pos == 0)
+ {
+ u1cmd = PGA11X_CMD_WRITE |
+ ((uint16_t)settings->channel << PGA11X_CHAN_SHIFT) |
+ ((uint16_t)settings->gain << PGA11X_GAIN_SHIFT);
+ u2cmd = PGA11X_DCCMD_NOOP;
+ }
+ else /* if (pos == 1) */
+ {
+ u1cmd = PGA11X_CMD_NOOP;
+ u2cmd = PGA11X_DCCMD_WRITE |
+ ((uint16_t)settings->channel << PGA11X_CHAN_SHIFT) |
+ ((uint16_t)settings->gain << PGA11X_GAIN_SHIFT);
+ }
+
+ /* Send the command */
+
+ pga11x_write(spi, u1cmd, u2cmd);
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: pga11x_read
+ *
+ * Description:
+ * Read from all PGA117 amplifier/multiplexers.
+ *
+ * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_read() reads from
+ * both chips in the daisy-chain. pga11x_uread() is provided to support
+ * accessing the parts independently.
+ *
+ * Input Parameters:
+ * spi - An SPI "bottom half" device driver instance
+ * settings - Returned channel and gain settings
+ *
+ * Returned Value:
+ * Zero on sucess; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int pga11x_read(PGA11X_HANDLE handle, FAR struct pga11x_settings_s *settings)
+{
+#ifdef CONFIG_PGA11X_DAISYCHAIN
+ FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle;
+ uint16_t u1value;
+ uint16_t u2value;
+
+ spivdbg("Entry\n");
+ DEBUGASSERT(handle && settings);
+
+ /* Lock the bus and read the configuration */
+
+ pga11x_lock(spi);
+
+ /* Select, send the 16-bit command, the 16-bit daisy-chain command, and
+ * then de-select the part. I do not know if de-selection between word
+ * transfers is required. However, it is shown in the timing diagrams
+ * for the part.
+ */
+
+ SPI_SELECT(spi, SPIDEV_MUX, true);
+ pga11x_send16(spi, PGA11X_CMD_READ);
+ pga11x_send16(spi, PGA11X_DCCMD_READ);
+ SPI_SELECT(spi, SPIDEV_MUX, false);
+
+ /* Re-select, get the returned values, de-select, and unlock */
+
+ SPI_SELECT(spi, SPIDEV_MUX, true);
+ u2value = pga11x_recv16(spi);
+ u1value = pga11x_recv16(spi);
+ SPI_SELECT(spi, SPIDEV_MUX, false);
+ pga11x_unlock(spi);
+
+ /* Decode the returned value */
+
+ spivdbg("Returning %04x %04x\n", u2value, u1value);
+ settings->u1.channel = (uint8_t)((u1value & PGA11X_CHAN_MASK) >> PGA11X_CHAN_SHIFT);
+ settings->u1.gain = (uint8_t)((u1value & PGA11X_GAIN_MASK) >> PGA11X_GAIN_SHIFT);
+ settings->u2.channel = (uint8_t)((u2value & PGA11X_CHAN_MASK) >> PGA11X_CHAN_SHIFT);
+ settings->u2.gain = (uint8_t)((u2value & PGA11X_GAIN_MASK) >> PGA11X_GAIN_SHIFT);
+ return OK;
+#else
+ FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle;
+ uint16_t value;
+
+ spivdbg("Entry\n");
+ DEBUGASSERT(handle);
+
+ /* Lock the bus and read the configuration */
+
+ pga11x_lock(spi);
+
+ /* Select, send the 16-bit PGA11X_CMD_READ command, and de-select. I do
+ * not know if de-selection between word transfers is required. However,
+ * it is shown in the timing diagrams for the part.
+ */
+
+ SPI_SELECT(spi, SPIDEV_MUX, true);
+ pga11x_send16(spi, PGA11X_CMD_READ);
+ SPI_SELECT(spi, SPIDEV_MUX, false);
+
+ /* Re-select, get the returned value, de-select, and unlock */
+
+ SPI_SELECT(spi, SPIDEV_MUX, true);
+ value = pga11x_recv16(spi);
+ SPI_SELECT(spi, SPIDEV_MUX, false);
+ pga11x_unlock(spi);
+
+ /* Decode the returned value */
+
+ spivdbg("Returning: %04x\n", value);
+ settings->channel = (uint8_t)((value & PGA11X_CHAN_MASK) >> PGA11X_CHAN_SHIFT);
+ settings->gain = (uint8_t)((value & PGA11X_GAIN_MASK) >> PGA11X_GAIN_SHIFT);
+ return OK;
+#endif
+}
+
+/****************************************************************************
+ * Name: pga11x_uread
+ *
+ * Description:
+ * Read from one PGA117 amplifier/multiplexer.
+ *
+ * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_read() reads
+ * the parts independently.
+ *
+ * Input Parameters:
+ * spi - An SPI "bottom half" device driver instance
+ * pos - Position of the chip in the daisy chain (0 or 1)
+ * settings - Returned channel and gain settings
+ *
+ * Returned Value:
+ * Zero on sucess; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PGA11X_DAISYCHAIN
+int pga11x_uread(PGA11X_HANDLE handle, int pos,
+ FAR struct pga11x_usettings_s *settings)
+{
+ struct pga11x_settings_s both;
+ int ret = pga11x_read(handle, &both);
+ if (ret == OK)
+ {
+ if (pos == 0)
+ {
+ settings->channel = both.u1.channel;
+ settings->gain = both.u1.gain;
+ }
+ else /* if (pos == 1) */
+ {
+ settings->channel = both.u2.channel;
+ settings->gain = both.u2.gain;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: pga11x_shutdown
+ *
+ * Description:
+ * Put all PGA11x's in shutdown down mode.
+ *
+ * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_shutdown() controls
+ * both chips in the daisy-chain. pga11x_ushutdown() is provided to
+ * control the parts independently.
+ *
+ * Input Parameters:
+ * spi - An SPI "bottom half" device driver instance
+ *
+ * Returned Value:
+ * Zero on sucess; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int pga11x_shutdown(PGA11X_HANDLE handle)
+{
+ FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle;
+
+ spivdbg("Entry\n");
+ DEBUGASSERT(handle);
+
+ /* Enter shutdown mode by issuing an SDN_EN command */
+
+#ifdef CONFIG_PGA11X_DAISYCHAIN
+ pga11x_write(spi, PGA11X_CMD_SDN_EN, PGA11X_DCCMD_SDN_EN);
+#else
+ pga11x_write(spi, PGA11X_CMD_SDN_EN);
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Name: pga11x_ushutdown
+ *
+ * Description:
+ * Put one PGA11x in shutdown down mode.
+ *
+ * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_ushutdown() is
+ * provided to shutdown the parts independently.
+ *
+ * Input Parameters:
+ * spi - An SPI "bottom half" device driver instance
+ * pos - Position of the chip in the daisy chain (0 or 1)
+ *
+ * Returned Value:
+ * Zero on sucess; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PGA11X_DAISYCHAIN
+int pga11x_ushutdown(PGA11X_HANDLE handle, int pos)
+{
+ FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle;
+
+ spivdbg("Entry\n");
+ DEBUGASSERT(handle);
+
+ /* Enter shutdown mode by issuing an SDN_EN command */
+
+ if (pos == 0)
+ {
+ pga11x_write(spi, PGA11X_CMD_SDN_EN, PGA11X_DCCMD_NOOP);
+ }
+ else
+ {
+ pga11x_write(spi, PGA11X_CMD_NOOP, PGA11X_DCCMD_SDN_EN);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: pga11x_enable
+ *
+ * Description:
+ * Take all PGA11x's out of shutdown down mode.
+ *
+ * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_enable() controls
+ * both chips in the daisy-chain. pga11x_uenable() is provided to
+ * control the parts independently.
+ *
+ * Input Parameters:
+ * spi - An SPI "bottom half" device driver instance
+ *
+ * Returned Value:
+ * Zero on sucess; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int pga11x_enable(PGA11X_HANDLE handle)
+{
+ FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle;
+
+ spivdbg("Entry\n");
+ DEBUGASSERT(handle);
+
+ /* Lock the bus and send the shutdown disable command. Shutdown mode is
+ * cleared (returned to the last valid write configuration) by the SDN_DIS
+ * command or by any valid Write command
+ */
+
+#ifdef CONFIG_PGA11X_DAISYCHAIN
+ pga11x_write(spi, PGA11X_CMD_SDN_DIS, PGA11X_DCCMD_SDN_DIS);
+#else
+ pga11x_write(spi, PGA11X_CMD_SDN_DIS);
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Name: pga11x_uenable
+ *
+ * Description:
+ * Take one PGA11x out of shutdown down mode.
+ *
+ * If CONFIG_PGA11X_DAISYCHAIN is defined, then pga11x_uenable() is
+ * provided to enable the parts independently.
+ *
+ * Input Parameters:
+ * spi - An SPI "bottom half" device driver instance
+ * pos - Position of the chip in the daisy chain (0 or 1)
+ *
+ * Returned Value:
+ * Zero on sucess; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PGA11X_DAISYCHAIN
+int pga11x_uenable(PGA11X_HANDLE handle, int pos)
+{
+ FAR struct spi_dev_s *spi = (FAR struct spi_dev_s *)handle;
+
+ spivdbg("Entry\n");
+ DEBUGASSERT(handle);
+
+ /* Enter shutdown mode by issuing an SDN_EN command */
+
+ if (pos == 0)
+ {
+ pga11x_write(spi, PGA11X_CMD_SDN_DIS, PGA11X_DCCMD_NOOP);
+ }
+ else
+ {
+ pga11x_write(spi, PGA11X_CMD_NOOP, PGA11X_DCCMD_SDN_DIS);
+ }
+
+ return OK;
+}
+#endif
+
+#endif /* CONFIG_ADC && CONFIG_ADC_PGA11X */
+
diff --git a/nuttx/drivers/bch/Kconfig b/nuttx/drivers/bch/Kconfig
new file mode 100644
index 000000000..ae2bf3130
--- /dev/null
+++ b/nuttx/drivers/bch/Kconfig
@@ -0,0 +1,4 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
diff --git a/nuttx/drivers/bch/Make.defs b/nuttx/drivers/bch/Make.defs
new file mode 100644
index 000000000..bc22df8e2
--- /dev/null
+++ b/nuttx/drivers/bch/Make.defs
@@ -0,0 +1,52 @@
+############################################################################
+# drivers/bch/Make.defs
+#
+# Copyright (C) 2008, 2011 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+ifneq ($(CONFIG_NFILE_DESCRIPTORS),0)
+ifneq ($(CONFIG_DISABLE_MOUNTPOINT),y)
+
+# Include BCH driver
+
+CSRCS += bchlib_setup.c bchlib_teardown.c bchlib_read.c bchlib_write.c \
+ bchlib_cache.c bchlib_sem.c bchdev_register.c bchdev_unregister.c \
+ bchdev_driver.c
+
+# Include BCH driver build support
+
+DEPPATH += --dep-path bch
+VPATH += :bch
+CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/bch}
+
+endif
+endif
diff --git a/nuttx/drivers/bch/bch_internal.h b/nuttx/drivers/bch/bch_internal.h
new file mode 100644
index 000000000..fb1c64236
--- /dev/null
+++ b/nuttx/drivers/bch/bch_internal.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+ * drivers/bch/bch_internal.h
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_BCH_INTERNAL_H
+#define __FS_BCH_INTERNAL_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <semaphore.h>
+#include <nuttx/fs/fs.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define bchlib_semgive(d) sem_post(&(d)->sem) /* To match bchlib_semtake */
+#define MAX_OPENCNT (255) /* Limit of uint8_t */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct bchlib_s
+{
+ struct inode *inode; /* I-node of the block driver */
+ sem_t sem; /* For atomic accesses to this structure */
+ size_t nsectors; /* Number of sectors supported by the device */
+ size_t sector; /* The current sector in the buffer */
+ uint16_t sectsize; /* The size of one sector on the device */
+ uint8_t refs; /* Number of references */
+ bool dirty; /* Data has been written to the buffer */
+ bool readonly; /* true: Only read operations are supported */
+ FAR uint8_t *buffer; /* One sector buffer */
+};
+
+/****************************************************************************
+ * Global Variables
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+EXTERN const struct file_operations bch_fops;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+EXTERN void bchlib_semtake(FAR struct bchlib_s *bch);
+EXTERN int bchlib_flushsector(FAR struct bchlib_s *bch);
+EXTERN int bchlib_readsector(FAR struct bchlib_s *bch, size_t sector);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __FS_BCH_INTERNAL_H */
diff --git a/nuttx/drivers/bch/bchdev_driver.c b/nuttx/drivers/bch/bchdev_driver.c
new file mode 100644
index 000000000..262a0af46
--- /dev/null
+++ b/nuttx/drivers/bch/bchdev_driver.c
@@ -0,0 +1,255 @@
+/****************************************************************************
+ * drivers/bch/bchdev_driver.c
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Compilation Switches
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/ioctl.h>
+
+#include "bch_internal.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int bch_open(FAR struct file *filp);
+static int bch_close(FAR struct file *filp);
+static ssize_t bch_read(FAR struct file *, FAR char *, size_t);
+static ssize_t bch_write(FAR struct file *, FAR const char *, size_t);
+static int bch_ioctl(FAR struct file *filp, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+const struct file_operations bch_fops =
+{
+ bch_open, /* open */
+ bch_close, /* close */
+ bch_read, /* read */
+ bch_write, /* write */
+ 0, /* seek */
+ bch_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , 0 /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bch_open
+ *
+ * Description: Open the block device
+ *
+ ****************************************************************************/
+
+static int bch_open(FAR struct file *filp)
+{
+ FAR struct inode *inode = filp->f_inode;
+ FAR struct bchlib_s *bch;
+
+ DEBUGASSERT(inode && inode->i_private);
+ bch = (FAR struct bchlib_s *)inode->i_private;
+
+ /* Increment the reference count */
+
+ bchlib_semtake(bch);
+ if (bch->refs == MAX_OPENCNT)
+ {
+ return -EMFILE;
+ }
+ else
+ {
+ bch->refs++;
+ }
+ bchlib_semgive(bch);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: bch_close
+ *
+ * Description: close the block device
+ *
+ ****************************************************************************/
+
+static int bch_close(FAR struct file *filp)
+{
+ FAR struct inode *inode = filp->f_inode;
+ FAR struct bchlib_s *bch;
+ int ret = OK;
+
+ DEBUGASSERT(inode && inode->i_private);
+ bch = (FAR struct bchlib_s *)inode->i_private;
+
+ /* Flush any dirty pages remaining in the cache */
+
+ bchlib_semtake(bch);
+ (void)bchlib_flushsector(bch);
+
+ /* Decrement the reference count (I don't use bchlib_decref() because I
+ * want the entire close operation to be atomic wrt other driver operations.
+ */
+
+ if (bch->refs == 0)
+ {
+ ret = -EIO;
+ }
+ else
+ {
+ bch->refs--;
+ }
+ bchlib_semgive(bch);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name:bch_read
+ ****************************************************************************/
+
+static ssize_t bch_read(FAR struct file *filp, FAR char *buffer, size_t len)
+{
+ FAR struct inode *inode = filp->f_inode;
+ FAR struct bchlib_s *bch;
+ int ret;
+
+ DEBUGASSERT(inode && inode->i_private);
+ bch = (FAR struct bchlib_s *)inode->i_private;
+
+ bchlib_semtake(bch);
+ ret = bchlib_read(bch, buffer, filp->f_pos, len);
+ if (ret > 0)
+ {
+ filp->f_pos += len;
+ }
+ bchlib_semgive(bch);
+ return ret;
+}
+
+/****************************************************************************
+ * Name:bch_write
+ ****************************************************************************/
+
+static ssize_t bch_write(FAR struct file *filp, FAR const char *buffer, size_t len)
+{
+ FAR struct inode *inode = filp->f_inode;
+ FAR struct bchlib_s *bch;
+ int ret = -EACCES;
+
+ DEBUGASSERT(inode && inode->i_private);
+ bch = (FAR struct bchlib_s *)inode->i_private;
+
+ if (!bch->readonly)
+ {
+ bchlib_semtake(bch);
+ ret = bchlib_write(bch, buffer, filp->f_pos, len);
+ if (ret > 0)
+ {
+ filp->f_pos += len;
+ }
+ bchlib_semgive(bch);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: bch_ioctl
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int bch_ioctl(FAR struct file *filp, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filp->f_inode;
+ FAR struct bchlib_s *bch;
+ int ret = -ENOTTY;
+
+ DEBUGASSERT(inode && inode->i_private);
+ bch = (FAR struct bchlib_s *)inode->i_private;
+
+ if (cmd == DIOC_GETPRIV)
+ {
+ FAR struct bchlib_s **bchr = (FAR struct bchlib_s **)((uintptr_t)arg);
+
+ bchlib_semtake(bch);
+ if (!bchr && bch->refs < 255)
+ {
+ ret = -EINVAL;
+ }
+ else
+ {
+ bch->refs++;
+ *bchr = bch;
+ }
+ bchlib_semgive(bch);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
diff --git a/nuttx/drivers/bch/bchdev_register.c b/nuttx/drivers/bch/bchdev_register.c
new file mode 100644
index 000000000..924977371
--- /dev/null
+++ b/nuttx/drivers/bch/bchdev_register.c
@@ -0,0 +1,104 @@
+/****************************************************************************
+ * drivers/bch/bchdev_register.c
+ *
+ * Copyright (C) 2008-2009, 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include "bch_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bchdev_register
+ *
+ * Description:
+ * Setup so that it exports the block driver referenced by 'blkdev' as a
+ * character device 'chardev'
+ *
+ ****************************************************************************/
+
+int bchdev_register(FAR const char *blkdev, FAR const char *chardev,
+ bool readonly)
+{
+ FAR void *handle;
+ int ret;
+
+ /* Setup the BCH lib functions */
+
+ ret = bchlib_setup(blkdev, readonly, &handle);
+ if (ret < 0)
+ {
+ fdbg("bchlib_setup failed: %d\n", -ret);
+ return ret;
+ }
+
+ /* Then setup the character device */
+
+ ret = register_driver(chardev, &bch_fops, 0666, handle);
+ if (ret < 0)
+ {
+ fdbg("register_driver failed: %d\n", -ret);
+ bchlib_teardown(handle);
+ handle = NULL;
+ }
+
+ return ret;
+}
diff --git a/nuttx/drivers/bch/bchdev_unregister.c b/nuttx/drivers/bch/bchdev_unregister.c
new file mode 100644
index 000000000..8c7360882
--- /dev/null
+++ b/nuttx/drivers/bch/bchdev_unregister.c
@@ -0,0 +1,160 @@
+/****************************************************************************
+ * drivers/bch/bchdev_unregister.c
+ *
+ * Copyright (C) 2008-2009, 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/ioctl.h>
+
+#include "bch_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bchdev_unregister
+ *
+ * Description:
+ * Unregister character driver access to a block device that was created
+ * by a previous call to bchdev_register().
+ *
+ ****************************************************************************/
+
+int bchdev_unregister(FAR const char *chardev)
+{
+ FAR struct bchlib_s *bch;
+ int fd;
+ int ret;
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (!chardev)
+ {
+ return -EINVAL;
+ }
+#endif
+
+ /* Open the character driver associated with chardev */
+
+ fd = open(chardev, O_RDONLY);
+ if (fd < 0)
+ {
+ dbg("Failed to open %s: %d\n", chardev, errno);
+ return -errno;
+ }
+
+ /* Get a reference to the internal data structure. On success, we
+ * will hold a reference count on the state structure.
+ */
+
+ ret = ioctl(fd, DIOC_GETPRIV, (unsigned long)((uintptr_t)&bch));
+ (void)close(fd);
+
+ if (ret < 0)
+ {
+ dbg("ioctl failed: %d\n", errno);
+ return -errno;
+ }
+
+ /* Lock out context switches. If there are no other references
+ * and no context switches, then we can assume that we can safely
+ * teardown the driver.
+ */
+
+ sched_lock();
+
+ /* Check if the internal structure is non-busy (we hold one reference). */
+
+ if (bch->refs > 1)
+ {
+ ret = -EBUSY;
+ goto errout_with_lock;
+ }
+
+ /* Unregister the driver (this cannot suspend or we lose our non-preemptive
+ * state!). Once the driver is successfully unregistered, we can assume
+ * we have exclusive access to the state instance.
+ */
+
+ ret = unregister_driver(chardev);
+ if (ret < 0)
+ {
+ goto errout_with_lock;
+ }
+
+ sched_unlock();
+
+ /* Release the internal structure */
+
+ bch->refs = 0;
+ return bchlib_teardown(bch);
+
+errout_with_lock:
+ bch->refs--;
+ sched_unlock();
+ return ret;
+}
diff --git a/nuttx/drivers/bch/bchlib_cache.c b/nuttx/drivers/bch/bchlib_cache.c
new file mode 100644
index 000000000..3b8212a13
--- /dev/null
+++ b/nuttx/drivers/bch/bchlib_cache.c
@@ -0,0 +1,133 @@
+/****************************************************************************
+ * drivers/bch/bchlib_cache.c
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+
+#include "bch_internal.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bchlib_flushsector
+ *
+ * Description:
+ * Flush the current contents of the sector buffer (if dirty)
+ *
+ * Assumptions:
+ * Caller must assume mutual exclusion
+ *
+ ****************************************************************************/
+
+int bchlib_flushsector(FAR struct bchlib_s *bch)
+{
+ FAR struct inode *inode;
+ ssize_t ret = OK;
+
+ if (bch->dirty)
+ {
+ inode = bch->inode;
+ ret = inode->u.i_bops->write(inode, bch->buffer, bch->sector, 1);
+ if (ret < 0)
+ {
+ fdbg("Write failed: %d\n");
+ }
+ bch->dirty = false;
+ }
+ return (int)ret;
+}
+
+/****************************************************************************
+ * Name: bchlib_readsector
+ *
+ * Description:
+ * Flush the current contents of the sector buffer (if dirty)
+ *
+ * Assumptions:
+ * Caller must assume mutual exclusion
+ *
+ ****************************************************************************/
+
+int bchlib_readsector(FAR struct bchlib_s *bch, size_t sector)
+{
+ FAR struct inode *inode;
+ ssize_t ret = OK;
+
+ if (bch->sector != sector)
+ {
+ inode = bch->inode;
+
+ (void)bchlib_flushsector(bch);
+ bch->sector = (size_t)-1;
+
+ ret = inode->u.i_bops->read(inode, bch->buffer, sector, 1);
+ if (ret < 0)
+ {
+ fdbg("Read failed: %d\n");
+ }
+ bch->sector = sector;
+ }
+ return (int)ret;
+}
+
diff --git a/nuttx/drivers/bch/bchlib_read.c b/nuttx/drivers/bch/bchlib_read.c
new file mode 100644
index 000000000..f4fad1096
--- /dev/null
+++ b/nuttx/drivers/bch/bchlib_read.c
@@ -0,0 +1,203 @@
+/****************************************************************************
+ * drivers/bch/bchlib_read.c
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+
+#include "bch_internal.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bchlib_read
+ *
+ * Description:
+ * Read from the block device set-up by bchlib_setup as if it were a character
+ * device.
+ *
+ ****************************************************************************/
+
+ssize_t bchlib_read(FAR void *handle, FAR char *buffer, size_t offset, size_t len)
+{
+ FAR struct bchlib_s *bch = (FAR struct bchlib_s *)handle;
+ size_t nsectors;
+ size_t sector;
+ uint16_t sectoffset;
+ size_t nbytes;
+ size_t bytesread;
+ int ret;
+
+ /* Get rid of this special case right away */
+
+ if (len < 1)
+ {
+ return 0;
+ }
+
+ /* Convert the file position into a sector number an offset. */
+
+ sector = offset / bch->sectsize;
+ sectoffset = offset - sector * bch->sectsize;
+
+ if (sector >= bch->nsectors)
+ {
+ /* Return end-of-file */
+
+ return 0;
+ }
+
+ /* Read the initial partial sector */
+
+ bytesread = 0;
+ if (sectoffset > 0)
+ {
+ /* Read the sector into the sector buffer */
+
+ bchlib_readsector(bch, sector);
+
+ /* Copy the tail end of the sector to the user buffer */
+
+ if (sectoffset + len > bch->sectsize)
+ {
+ nbytes = bch->sectsize - sectoffset;
+ }
+ else
+ {
+ nbytes = len;
+ }
+
+ memcpy(buffer, &bch->buffer[sectoffset], nbytes);
+
+ /* Adjust pointers and counts */
+
+ sectoffset = 0;
+ sector++;
+
+ if (sector >= bch->nsectors)
+ {
+ return nbytes;
+ }
+
+ bytesread = nbytes;
+ buffer += nbytes;
+ len -= nbytes;
+ }
+
+ /* Then read all of the full sectors following the partial sector directly
+ * into the user buffer.
+ */
+
+ if (len >= bch->sectsize )
+ {
+ nsectors = len / bch->sectsize;
+ if (sector + nsectors > bch->nsectors)
+ {
+ nsectors = bch->nsectors - sector;
+ }
+
+ ret = bch->inode->u.i_bops->read(bch->inode, (FAR uint8_t *)buffer,
+ sector, nsectors);
+ if (ret < 0)
+ {
+ fdbg("Read failed: %d\n");
+ return ret;
+ }
+
+ /* Adjust pointers and counts */
+
+ sectoffset = 0;
+ sector += nsectors;
+
+ nbytes = nsectors * bch->sectsize;
+ bytesread += nbytes;
+
+ if (sector >= bch->nsectors)
+ {
+ return bytesread;
+ }
+
+ buffer += nbytes;
+ len -= nbytes;
+ }
+
+ /* Then read any partial final sector */
+
+ if (len > 0)
+ {
+ /* Read the sector into the sector buffer */
+
+ bchlib_readsector(bch, sector);
+
+ /* Copy the head end of the sector to the user buffer */
+
+ memcpy(buffer, bch->buffer, len);
+
+ /* Adjust counts */
+
+ bytesread += len;
+ }
+
+ return bytesread;
+}
diff --git a/nuttx/drivers/bch/bchlib_sem.c b/nuttx/drivers/bch/bchlib_sem.c
new file mode 100644
index 000000000..1698ed0a5
--- /dev/null
+++ b/nuttx/drivers/bch/bchlib_sem.c
@@ -0,0 +1,84 @@
+/****************************************************************************
+ * drivers/bch/bchlib_sem.c
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include "bch_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bch_semtake
+ ****************************************************************************/
+
+void bchlib_semtake(FAR struct bchlib_s *bch)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&bch->sem) != 0)
+ {
+ /* The only case that an error should occur here is if
+ * the wait was awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
diff --git a/nuttx/drivers/bch/bchlib_setup.c b/nuttx/drivers/bch/bchlib_setup.c
new file mode 100644
index 000000000..1026248b5
--- /dev/null
+++ b/nuttx/drivers/bch/bchlib_setup.c
@@ -0,0 +1,159 @@
+/****************************************************************************
+ * drivers/bch/bchlib_setup.c
+ *
+ * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+
+#include "bch_internal.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bchlib_setup
+ *
+ * Description:
+ * Setup so that the block driver referenced by 'blkdev' can be accessed
+ * similar to a character device.
+ *
+ ****************************************************************************/
+
+int bchlib_setup(const char *blkdev, bool readonly, FAR void **handle)
+{
+ FAR struct bchlib_s *bch;
+ struct geometry geo;
+ int ret;
+
+ DEBUGASSERT(blkdev);
+
+ /* Allocate the BCH state structure */
+
+ bch = (FAR struct bchlib_s*)kzalloc(sizeof(struct bchlib_s));
+ if (!bch)
+ {
+ fdbg("Failed to allocate BCH structure\n");
+ return -ENOMEM;
+ }
+
+ /* Open the block driver */
+
+ ret = open_blockdriver(blkdev, readonly ? MS_RDONLY : 0, &bch->inode);
+ if (ret < 0)
+ {
+ fdbg("Failed to open driver %s: %d\n", blkdev, -ret);
+ goto errout_with_bch;
+ }
+
+ DEBUGASSERT(bch->inode && bch->inode->u.i_bops && bch->inode->u.i_bops->geometry);
+
+ ret = bch->inode->u.i_bops->geometry(bch->inode, &geo);
+ if (ret < 0)
+ {
+ fdbg("geometry failed: %d\n", -ret);
+ goto errout_with_bch;
+ }
+
+ if (!geo.geo_available)
+ {
+ fdbg("geometry failed: %d\n", -ret);
+ ret = -ENODEV;
+ goto errout_with_bch;
+ }
+
+ if (!readonly && (!bch->inode->u.i_bops->write || !geo.geo_writeenabled))
+ {
+ fdbg("write access not supported\n");
+ ret = -EACCES;
+ goto errout_with_bch;
+ }
+
+ /* Save the geometry info and complete initialization of the structure */
+
+ sem_init(&bch->sem, 0, 1);
+ bch->nsectors = geo.geo_nsectors;
+ bch->sectsize = geo.geo_sectorsize;
+ bch->sector = (size_t)-1;
+ bch->readonly = readonly;
+
+ /* Allocate the sector I/O buffer */
+
+ bch->buffer = (FAR uint8_t *)kmalloc(bch->sectsize);
+ if (!bch->buffer)
+ {
+ fdbg("Failed to allocate sector buffer\n");
+ ret = -ENOMEM;
+ goto errout_with_bch;
+ }
+
+ *handle = bch;
+ return OK;
+
+errout_with_bch:
+ kfree(bch);
+ return ret;
+}
diff --git a/nuttx/drivers/bch/bchlib_teardown.c b/nuttx/drivers/bch/bchlib_teardown.c
new file mode 100644
index 000000000..8657c4a69
--- /dev/null
+++ b/nuttx/drivers/bch/bchlib_teardown.c
@@ -0,0 +1,113 @@
+/****************************************************************************
+ * drivers/bch/bchlib_teardown.c
+ *
+ * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+
+#include "bch_internal.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bchlib_teardown
+ *
+ * Description:
+ * Setup so that the block driver referenced by 'blkdev' can be accessed
+ * similar to a character device.
+ *
+ ****************************************************************************/
+
+int bchlib_teardown(FAR void *handle)
+{
+ FAR struct bchlib_s *bch = (FAR struct bchlib_s *)handle;
+
+ DEBUGASSERT(handle);
+
+ /* Check that there are not outstanding reference counts on the object */
+
+ if (bch->refs > 0)
+ {
+ return -EBUSY;
+ }
+
+ /* Flush any pending data to the block driver */
+
+ bchlib_flushsector(bch);
+
+ /* Close the block driver */
+
+ (void)close_blockdriver(bch->inode);
+
+ /* Free the BCH state structure */
+
+ if (bch->buffer)
+ {
+ kfree(bch->buffer);
+ }
+
+ sem_destroy(&bch->sem);
+ kfree(bch);
+ return OK;
+}
+
diff --git a/nuttx/drivers/bch/bchlib_write.c b/nuttx/drivers/bch/bchlib_write.c
new file mode 100644
index 000000000..8d7dcf26f
--- /dev/null
+++ b/nuttx/drivers/bch/bchlib_write.c
@@ -0,0 +1,216 @@
+/****************************************************************************
+ * drivers/bch/bchlib_write.c
+ *
+ * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+
+#include "bch_internal.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bchlib_write
+ *
+ * Description:
+ * Write to the block device set-up by bchlib_setup as if it were a character
+ * device.
+ *
+ ****************************************************************************/
+
+ssize_t bchlib_write(FAR void *handle, FAR const char *buffer, size_t offset, size_t len)
+{
+ FAR struct bchlib_s *bch = (FAR struct bchlib_s *)handle;
+ size_t nsectors;
+ size_t sector;
+ uint16_t sectoffset;
+ size_t nbytes;
+ size_t byteswritten;
+ int ret;
+
+ /* Get rid of this special case right away */
+
+ if (len < 1)
+ {
+ return 0;
+ }
+
+ /* Convert the file position into a sector number an offset. */
+
+ sector = offset / bch->sectsize;
+ sectoffset = offset - sector * bch->sectsize;
+
+ if (sector >= bch->nsectors)
+ {
+ return -EFBIG;
+ }
+
+ /* Write the initial partial sector */
+
+ byteswritten = 0;
+ if (sectoffset > 0)
+ {
+ /* Read the full sector into the sector buffer */
+
+ bchlib_readsector(bch, sector);
+
+ /* Copy the tail end of the sector from the user buffer */
+
+ if (sectoffset + len > bch->sectsize)
+ {
+ nbytes = bch->sectsize - sectoffset;
+ }
+ else
+ {
+ nbytes = len;
+ }
+
+ memcpy(&bch->buffer[sectoffset], buffer, nbytes);
+ bch->dirty = true;
+
+ /* Adjust pointers and counts */
+
+ sectoffset = 0;
+ sector++;
+
+ if (sector >= bch->nsectors)
+ {
+ return nbytes;
+ }
+
+ byteswritten = nbytes;
+ buffer += nbytes;
+ len -= nbytes;
+ }
+
+ /* Then write all of the full sectors following the partial sector
+ * directly from the user buffer.
+ */
+
+ if (len >= bch->sectsize )
+ {
+ nsectors = len / bch->sectsize;
+ if (sector + nsectors > bch->nsectors)
+ {
+ nsectors = bch->nsectors - sector;
+ }
+
+ /* Write the contiguous sectors */
+
+ ret = bch->inode->u.i_bops->write(bch->inode, (FAR uint8_t *)buffer,
+ sector, nsectors);
+ if (ret < 0)
+ {
+ fdbg("Write failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Adjust pointers and counts */
+
+ sectoffset = 0;
+ sector += nsectors;
+
+ nbytes = nsectors * bch->sectsize;
+ byteswritten += nbytes;
+
+ if (sector >= bch->nsectors)
+ {
+ return byteswritten;
+ }
+
+ buffer += nbytes;
+ len -= nbytes;
+ }
+
+ /* Then write any partial final sector */
+
+ if (len > 0)
+ {
+ /* Read the sector into the sector buffer */
+
+ bchlib_readsector(bch, sector);
+
+ /* Copy the head end of the sector from the user buffer */
+
+ memcpy(bch->buffer, buffer, len);
+ bch->dirty = true;
+
+ /* Adjust counts */
+
+ byteswritten += len;
+ }
+
+ /* Finally, flush any cached writes to the device as well */
+
+ ret = bchlib_flushsector(bch);
+ if (ret < 0)
+ {
+ fdbg("Flush failed: %d\n", ret);
+ return ret;
+ }
+
+ return byteswritten;
+}
+
diff --git a/nuttx/drivers/can.c b/nuttx/drivers/can.c
new file mode 100644
index 000000000..2c1567781
--- /dev/null
+++ b/nuttx/drivers/can.c
@@ -0,0 +1,845 @@
+/****************************************************************************
+ * drivers/can.c
+ *
+ * Copyright (C) 2008-2009, 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/can.h>
+
+#include <arch/irq.h>
+
+#ifdef CONFIG_CAN
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Debug ********************************************************************/
+/* Non-standard debug that may be enabled just for testing CAN */
+
+#ifdef CONFIG_DEBUG_CAN
+# define candbg dbg
+# define canvdbg vdbg
+# define canlldbg lldbg
+# define canllvdbg llvdbg
+#else
+# define candbg(x...)
+# define canvdbg(x...)
+# define canlldbg(x...)
+# define canllvdbg(x...)
+#endif
+
+/* Timing Definitions *******************************************************/
+
+#define HALF_SECOND_MSEC 500
+#define HALF_SECOND_USEC 500000L
+
+/****************************************************************************
+ * Private Type Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int can_open(FAR struct file *filep);
+static int can_close(FAR struct file *filep);
+static ssize_t can_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
+static int can_xmit(FAR struct can_dev_s *dev);
+static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+static inline ssize_t can_rtrread(FAR struct can_dev_s *dev, FAR struct canioctl_rtr_s *rtr);
+static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_canops =
+{
+ can_open, /* open */
+ can_close, /* close */
+ can_read, /* read */
+ can_write, /* write */
+ 0, /* seek */
+ can_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , 0 /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/************************************************************************************
+ * Name: can_open
+ *
+ * Description:
+ * This function is called whenever the CAN device is opened.
+ *
+ ************************************************************************************/
+
+static int can_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct can_dev_s *dev = inode->i_private;
+ uint8_t tmp;
+ int ret = OK;
+
+ canvdbg("ocount: %d\n", dev->cd_ocount);
+
+ /* If the port is the middle of closing, wait until the close is finished */
+
+ if (sem_wait(&dev->cd_closesem) != OK)
+ {
+ ret = -errno;
+ }
+ else
+ {
+ /* Increment the count of references to the device. If this the first
+ * time that the driver has been opened for this device, then initialize
+ * the device.
+ */
+
+ tmp = dev->cd_ocount + 1;
+ if (tmp == 0)
+ {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ }
+ else
+ {
+ /* Check if this is the first time that the driver has been opened. */
+
+ if (tmp == 1)
+ {
+ /* Yes.. perform one time hardware initialization. */
+
+ irqstate_t flags = irqsave();
+ ret = dev_setup(dev);
+ if (ret == OK)
+ {
+ /* Mark the FIFOs empty */
+
+ dev->cd_xmit.tx_head = 0;
+ dev->cd_xmit.tx_queue = 0;
+ dev->cd_xmit.tx_tail = 0;
+ dev->cd_recv.rx_head = 0;
+ dev->cd_recv.rx_tail = 0;
+
+ /* Finally, Enable the CAN RX interrupt */
+
+ dev_rxint(dev, true);
+
+ /* Save the new open count on success */
+
+ dev->cd_ocount = tmp;
+ }
+ irqrestore(flags);
+ }
+ }
+ sem_post(&dev->cd_closesem);
+ }
+ return ret;
+}
+
+/************************************************************************************
+ * Name: can_close
+ *
+ * Description:
+ * This routine is called when the CAN device is closed.
+ * It waits for the last remaining data to be sent.
+ *
+ ************************************************************************************/
+
+static int can_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct can_dev_s *dev = inode->i_private;
+ irqstate_t flags;
+ int ret = OK;
+
+ canvdbg("ocount: %d\n", dev->cd_ocount);
+
+ if (sem_wait(&dev->cd_closesem) != OK)
+ {
+ ret = -errno;
+ }
+ else
+ {
+ /* Decrement the references to the driver. If the reference count will
+ * decrement to 0, then uninitialize the driver.
+ */
+
+ if (dev->cd_ocount > 1)
+ {
+ dev->cd_ocount--;
+ sem_post(&dev->cd_closesem);
+ }
+ else
+ {
+ /* There are no more references to the port */
+
+ dev->cd_ocount = 0;
+
+ /* Stop accepting input */
+
+ dev_rxint(dev, false);
+
+ /* Now we wait for the transmit FIFO to clear */
+
+ while (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail)
+ {
+#ifndef CONFIG_DISABLE_SIGNALS
+ usleep(HALF_SECOND_USEC);
+#else
+ up_mdelay(HALF_SECOND_MSEC);
+#endif
+ }
+
+ /* And wait for the TX hardware FIFO to drain */
+
+ while (!dev_txempty(dev))
+ {
+#ifndef CONFIG_DISABLE_SIGNALS
+ usleep(HALF_SECOND_USEC);
+#else
+ up_mdelay(HALF_SECOND_MSEC);
+#endif
+ }
+
+ /* Free the IRQ and disable the CAN device */
+
+ flags = irqsave(); /* Disable interrupts */
+ dev_shutdown(dev); /* Disable the CAN */
+ irqrestore(flags);
+
+ sem_post(&dev->cd_closesem);
+ }
+ }
+ return ret;
+}
+
+/************************************************************************************
+ * Name: can_read
+ *
+ * Description:
+ * Read standard CAN messages
+ *
+ ************************************************************************************/
+
+static ssize_t can_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct can_dev_s *dev = inode->i_private;
+ size_t nread;
+ irqstate_t flags;
+ int ret = 0;
+
+ canvdbg("buflen: %d\n", buflen);
+
+ /* The caller must provide enough memory to catch the smallest possible message
+ * This is not a system error condition, but we won't permit it, Hence we return 0.
+ */
+
+ if (buflen >= CAN_MSGLEN(0))
+ {
+ /* Interrupts must be disabled while accessing the cd_recv FIFO */
+
+ flags = irqsave();
+ while (dev->cd_recv.rx_head == dev->cd_recv.rx_tail)
+ {
+ /* The receive FIFO is empty -- was non-blocking mode selected? */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ ret = -EAGAIN;
+ goto return_with_irqdisabled;
+ }
+
+ /* Wait for a message to be received */
+
+ ret = sem_wait(&dev->cd_recv.rx_sem);
+ if (ret < 0)
+ {
+ ret = -errno;
+ goto return_with_irqdisabled;
+ }
+ }
+
+ /* The cd_recv FIFO is not empty. Copy all buffered data that will fit
+ * in the user buffer.
+ */
+
+ nread = 0;
+ do
+ {
+ /* Will the next message in the FIFO fit into the user buffer? */
+
+ FAR struct can_msg_s *msg = &dev->cd_recv.rx_buffer[dev->cd_recv.rx_head];
+ int msglen = CAN_MSGLEN(msg->cm_hdr.ch_dlc);
+
+ if (nread + msglen > buflen)
+ {
+ break;
+ }
+
+ /* Copy the message to the user buffer */
+
+ memcpy(&buffer[nread], msg, msglen);
+ nread += msglen;
+
+ /* Increment the head of the circular message buffer */
+
+ if (++dev->cd_recv.rx_head >= CONFIG_CAN_FIFOSIZE)
+ {
+ dev->cd_recv.rx_head = 0;
+ }
+ }
+ while (dev->cd_recv.rx_head != dev->cd_recv.rx_tail);
+
+ /* All on the messages have bee transferred. Return the number of bytes
+ * that were read.
+ */
+
+ ret = nread;
+
+return_with_irqdisabled:
+ irqrestore(flags);
+ }
+ return ret;
+}
+
+/************************************************************************************
+ * Name: can_xmit
+ *
+ * Description:
+ * Send the message at the head of the cd_xmit FIFO
+ *
+ * Assumptions:
+ * Called with interrupts disabled
+ *
+ ************************************************************************************/
+
+static int can_xmit(FAR struct can_dev_s *dev)
+{
+ int tmpndx;
+ int ret = -EBUSY;
+
+ canllvdbg("xmit head: %d queue: %d tail: %d\n",
+ dev->cd_xmit.tx_head, dev->cd_xmit.tx_queue, dev->cd_xmit.tx_tail);
+
+ /* If there is nothing to send, then just disable interrupts and return */
+
+ if (dev->cd_xmit.tx_head == dev->cd_xmit.tx_tail)
+ {
+ DEBUGASSERT(dev->cd_xmit.tx_queue == dev->cd_xmit.tx_head);
+ dev_txint(dev, false);
+ return -EIO;
+ }
+
+ /* Check if we have already queued all of the data in the TX fifo.
+ *
+ * tx_tail: Incremented in can_write each time a message is queued in the FIFO
+ * tx_head: Incremented in can_txdone each time a message completes
+ * tx_queue: Incremented each time that a message is sent to the hardware.
+ *
+ * Logically (ignoring buffer wrap-around): tx_head <= tx_queue <= tx_tail
+ * tx_head == tx_queue == tx_tail means that the FIFO is empty
+ * tx_head < tx_queue == tx_tail means that all data has been queued, but
+ * we are still waiting for transmissions to complete.
+ */
+
+ while (dev->cd_xmit.tx_queue != dev->cd_xmit.tx_tail && dev_txready(dev))
+ {
+ /* No.. The fifo should not be empty in this case */
+
+ DEBUGASSERT(dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail);
+
+ /* Increment the FIFO queue index before sending (because dev_send()
+ * might call can_txdone().
+ */
+
+ tmpndx = dev->cd_xmit.tx_queue;
+ if (++dev->cd_xmit.tx_queue >= CONFIG_CAN_FIFOSIZE)
+ {
+ dev->cd_xmit.tx_queue = 0;
+ }
+
+ /* Send the next message at the FIFO queue index */
+
+ ret = dev_send(dev, &dev->cd_xmit.tx_buffer[tmpndx]);
+ if (ret != OK)
+ {
+ candbg("dev_send failed: %d\n", ret);
+ break;
+ }
+ }
+
+ /* Make sure that TX interrupts are enabled */
+
+ dev_txint(dev, true);
+ return ret;
+}
+
+/************************************************************************************
+ * Name: can_write
+ ************************************************************************************/
+
+static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct can_dev_s *dev = inode->i_private;
+ FAR struct can_txfifo_s *fifo = &dev->cd_xmit;
+ FAR struct can_msg_s *msg;
+ bool inactive;
+ ssize_t nsent = 0;
+ irqstate_t flags;
+ int nexttail;
+ int msglen;
+ int ret = 0;
+
+ canvdbg("buflen: %d\n", buflen);
+
+ /* Interrupts must disabled throughout the following */
+
+ flags = irqsave();
+
+ /* Check if the TX is inactive when we started. In certain race conditionas, there
+ * may be a pending interrupt to kick things back off, but we will here that there
+ * is not. That the hardware is IDLE and will need to be kick-started.
+ */
+
+ inactive = dev_txempty(dev);
+
+ /* Add the messages to the FIFO. Ignore any trailing messages that are
+ * shorter than the minimum.
+ */
+
+ while ((buflen - nsent) >= CAN_MSGLEN(0))
+ {
+ /* Check if adding this new message would over-run the drivers ability to enqueue
+ * xmit data.
+ */
+
+ nexttail = fifo->tx_tail + 1;
+ if (nexttail >= CONFIG_CAN_FIFOSIZE)
+ {
+ nexttail = 0;
+ }
+
+ /* If the XMIT fifo becomes full, then wait for space to become available */
+
+ while (nexttail == fifo->tx_head)
+ {
+ /* The transmit FIFO is full -- was non-blocking mode selected? */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ if (nsent == 0)
+ {
+ ret = -EAGAIN;
+ }
+ else
+ {
+ ret = nsent;
+ }
+ goto return_with_irqdisabled;
+ }
+
+ /* If the TX hardware was inactive when we started, then we will have
+ * start the XMIT sequence generate the TX done interrrupts needed
+ * to clear the FIFO.
+ */
+
+ if (inactive)
+ {
+ can_xmit(dev);
+ }
+
+ /* Wait for a message to be sent */
+
+ do
+ {
+ DEBUGASSERT(dev->cd_ntxwaiters < 255);
+ dev->cd_ntxwaiters++;
+ ret = sem_wait(&fifo->tx_sem);
+ dev->cd_ntxwaiters--;
+
+ if (ret < 0 && errno != EINTR)
+ {
+ ret = -errno;
+ goto return_with_irqdisabled;
+ }
+ }
+ while (ret < 0);
+
+ /* Re-check the FIFO state */
+
+ inactive = dev_txempty(dev);
+ }
+
+ /* We get here if there is space at the end of the FIFO. Add the new
+ * CAN message at the tail of the FIFO.
+ */
+
+ msg = (FAR struct can_msg_s *)&buffer[nsent];
+ msglen = CAN_MSGLEN(msg->cm_hdr.ch_dlc);
+ memcpy(&fifo->tx_buffer[fifo->tx_tail], msg, msglen);
+
+ /* Increment the tail of the circular buffer */
+
+ fifo->tx_tail = nexttail;
+
+ /* Increment the number of bytes that were sent */
+
+ nsent += msglen;
+ }
+
+ /* We get here after all messages have been added to the FIFO. Check if
+ * we need to kick of the XMIT sequence.
+ */
+
+ if (inactive)
+ {
+ can_xmit(dev);
+ }
+
+ /* Return the number of bytes that were sent */
+
+ ret = nsent;
+
+return_with_irqdisabled:
+ irqrestore(flags);
+ return ret;
+}
+
+/************************************************************************************
+ * Name: can_rtrread
+ *
+ * Description:
+ * Read RTR messages. The RTR message is a special message -- it is an outgoing
+ * message that says "Please re-transmit the message with the same identifier as
+ * this message. So the RTR read is really a send-wait-receive operation.
+ *
+ ************************************************************************************/
+
+static inline ssize_t can_rtrread(FAR struct can_dev_s *dev, FAR struct canioctl_rtr_s *rtr)
+{
+ FAR struct can_rtrwait_s *wait = NULL;
+ irqstate_t flags;
+ int i;
+ int ret = -ENOMEM;
+
+ /* Disable interrupts through this operation */
+
+ flags = irqsave();
+
+ /* Find an avaiable slot in the pending RTR list */
+
+ for (i = 0; i < CONFIG_CAN_NPENDINGRTR; i++)
+ {
+ FAR struct can_rtrwait_s *tmp = &dev->cd_rtr[i];
+ if (!rtr->ci_msg)
+ {
+ tmp->cr_id = rtr->ci_id;
+ tmp->cr_msg = rtr->ci_msg;
+ dev->cd_npendrtr++;
+ wait = tmp;
+ break;
+ }
+ }
+
+ if (wait)
+ {
+ /* Send the remote transmission request */
+
+ ret = dev_remoterequest(dev, wait->cr_id);
+ if (ret == OK)
+ {
+ /* Then wait for the response */
+
+ ret = sem_wait(&wait->cr_sem);
+ }
+ }
+ irqrestore(flags);
+ return ret;
+}
+
+/************************************************************************************
+ * Name: can_ioctl
+ ************************************************************************************/
+
+static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct can_dev_s *dev = inode->i_private;
+ int ret = OK;
+
+ canvdbg("cmd: %d arg: %ld\n", cmd, arg);
+
+ /* Handle built-in ioctl commands */
+
+ switch (cmd)
+ {
+ /* CANIOCTL_RTR: Send the remote transmission request and wait for the response.
+ * Argument is a reference to struct canioctl_rtr_s (casting to uintptr_t first
+ * eliminates complaints on some architectures where the sizeof long is different
+ * from the size of a pointer).
+ */
+
+ case CANIOCTL_RTR:
+ ret = can_rtrread(dev, (struct canioctl_rtr_s*)((uintptr_t)arg));
+ break;
+
+ /* Not a "built-in" ioctl command.. perhaps it is unique to this device driver */
+
+ default:
+ ret = dev_ioctl(dev, cmd, arg);
+ break;
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/************************************************************************************
+ * Name: can_register
+ *
+ * Description:
+ * Register serial console and serial ports.
+ *
+ ************************************************************************************/
+
+int can_register(FAR const char *path, FAR struct can_dev_s *dev)
+{
+ int i;
+
+ /* Initialize the CAN device structure */
+
+ dev->cd_ocount = 0;
+
+ sem_init(&dev->cd_xmit.tx_sem, 0, 0);
+ sem_init(&dev->cd_recv.rx_sem, 0, 0);
+ sem_init(&dev->cd_closesem, 0, 1);
+
+ for (i = 0; i < CONFIG_CAN_NPENDINGRTR; i++)
+ {
+ sem_init(&dev->cd_rtr[i].cr_sem, 0, 0);
+ dev->cd_rtr[i].cr_msg = NULL;
+ dev->cd_npendrtr--;
+ }
+
+ /* Initialize/reset the CAN hardware */
+
+ dev_reset(dev);
+
+ /* Register the CAN device */
+
+ canvdbg("Registering %s\n", path);
+ return register_driver(path, &g_canops, 0666, dev);
+}
+
+/************************************************************************************
+ * Name: can_receive
+ *
+ * Description:
+ * Called from the CAN interrupt handler when new read data is available
+ *
+ * Parameters:
+ * dev - CAN driver state structure
+ * hdr - CAN message header
+ * data - CAN message data (if DLC > 0)
+ *
+ * Assumptions:
+ * CAN interrupts are disabled.
+ *
+ ************************************************************************************/
+
+int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr, FAR uint8_t *data)
+{
+ FAR struct can_rxfifo_s *fifo = &dev->cd_recv;
+ FAR uint8_t *dest;
+ int nexttail;
+ int err = -ENOMEM;
+ int i;
+
+ canllvdbg("ID: %d DLC: %d\n", hdr->ch_id, hdr->ch_dlc);
+
+ /* Check if adding this new message would over-run the drivers ability to enqueue
+ * read data.
+ */
+
+ nexttail = fifo->rx_tail + 1;
+ if (nexttail >= CONFIG_CAN_FIFOSIZE)
+ {
+ nexttail = 0;
+ }
+
+ /* First, check if this response matches any RTR response that we may be waiting for */
+
+ if (dev->cd_npendrtr > 0)
+ {
+ /* There are pending RTR requests -- search the lists of requests
+ * and see any any matches this new message.
+ */
+
+ for (i = 0; i < CONFIG_CAN_NPENDINGRTR; i++)
+ {
+ FAR struct can_rtrwait_s *rtr = &dev->cd_rtr[i];
+ FAR struct can_msg_s *msg = rtr->cr_msg;
+
+ /* Check if the entry is valid and if the ID matches. A valid entry has
+ * a non-NULL receiving address
+ */
+
+ if (msg && hdr->ch_id == rtr->cr_id)
+ {
+ /* We have the response... copy the data to the user's buffer */
+
+ memcpy(&msg->cm_hdr, hdr, sizeof(struct can_hdr_s));
+ for (i = 0, dest = msg->cm_data; i < hdr->ch_dlc; i++)
+ {
+ *dest++ = *data++;
+ }
+
+ /* Mark the entry unused */
+
+ rtr->cr_msg = NULL;
+
+ /* And restart the waiting thread */
+
+ sem_post(&rtr->cr_sem);
+ }
+ }
+ }
+
+ /* Refuse the new data if the FIFO is full */
+
+ if (nexttail != fifo->rx_head)
+ {
+ /* Add the new, decoded CAN message at the tail of the FIFO */
+
+ memcpy(&fifo->rx_buffer[fifo->rx_tail].cm_hdr, hdr, sizeof(struct can_hdr_s));
+ for (i = 0, dest = fifo->rx_buffer[fifo->rx_tail].cm_data; i < hdr->ch_dlc; i++)
+ {
+ *dest++ = *data++;
+ }
+
+ /* Increment the tail of the circular buffer */
+
+ fifo->rx_tail = nexttail;
+
+ /* The increment the counting semaphore. The maximum value should be
+ * CONFIG_CAN_FIFOSIZE -- one possible count for each allocated message buffer.
+ */
+
+ sem_post(&fifo->rx_sem);
+ err = OK;
+ }
+ return err;
+}
+
+/************************************************************************************
+ * Name: can_txdone
+ *
+ * Description:
+ * Called from the CAN interrupt handler at the completion of a send operation.
+ *
+ * Parameters:
+ * dev - The specific CAN device
+ * hdr - The 16-bit CAN header
+ * data - An array contain the CAN data.
+ *
+ * Return:
+ * OK on success; a negated errno on failure.
+ *
+ ************************************************************************************/
+
+int can_txdone(FAR struct can_dev_s *dev)
+{
+ int ret = -ENOENT;
+
+ canllvdbg("xmit head: %d queue: %d tail: %d\n",
+ dev->cd_xmit.tx_head, dev->cd_xmit.tx_queue, dev->cd_xmit.tx_tail);
+
+ /* Verify that the xmit FIFO is not empty */
+
+ if (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail)
+ {
+ DEBUGASSERT(dev->cd_xmit.tx_head != dev->cd_xmit.tx_queue);
+
+ /* Remove the message at the head of the xmit FIFO */
+
+ if (++dev->cd_xmit.tx_head >= CONFIG_CAN_FIFOSIZE)
+ {
+ dev->cd_xmit.tx_head = 0;
+ }
+
+ /* Send the next message in the FIFO */
+
+ ret = can_xmit(dev);
+
+ /* Are there any threads waiting for space in the TX FIFO? */
+
+ if (ret == OK && dev->cd_ntxwaiters > 0)
+ {
+ /* Yes.. Inform them that new xmit space is available */
+
+ ret = sem_post(&dev->cd_xmit.tx_sem);
+ }
+ }
+ return ret;
+}
+
+#endif /* CONFIG_CAN */
diff --git a/nuttx/drivers/dev_null.c b/nuttx/drivers/dev_null.c
new file mode 100644
index 000000000..c70370e1e
--- /dev/null
+++ b/nuttx/drivers/dev_null.c
@@ -0,0 +1,135 @@
+/****************************************************************************
+ * drivers/dev_null.c
+ *
+ * Copyright (C) 2007, 2008 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <string.h>
+#include <poll.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static ssize_t devnull_read(FAR struct file *, FAR char *, size_t);
+static ssize_t devnull_write(FAR struct file *, FAR const char *, size_t);
+#ifndef CONFIG_DISABLE_POLL
+static int devnull_poll(FAR struct file *filp, FAR struct pollfd *fds,
+ bool setup);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations devnull_fops =
+{
+ 0, /* open */
+ 0, /* close */
+ devnull_read, /* read */
+ devnull_write, /* write */
+ 0, /* seek */
+ 0 /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , devnull_poll /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: devnull_read
+ ****************************************************************************/
+
+static ssize_t devnull_read(FAR struct file *filp, FAR char *buffer, size_t len)
+{
+ return 0; /* Return EOF */
+}
+
+/****************************************************************************
+ * Name: devnull_write
+ ****************************************************************************/
+
+static ssize_t devnull_write(FAR struct file *filp, FAR const char *buffer, size_t len)
+{
+ return len; /* Say that everything was written */
+}
+
+/****************************************************************************
+ * Name: devnull_poll
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static int devnull_poll(FAR struct file *filp, FAR struct pollfd *fds,
+ bool setup)
+{
+ if (setup)
+ {
+ fds->revents |= (fds->events & (POLLIN|POLLOUT));
+ if (fds->revents != 0)
+ {
+ sem_post(fds->sem);
+ }
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: devnull_register
+ *
+ * Description:
+ * Register /dev/null
+ *
+ ****************************************************************************/
+
+void devnull_register(void)
+{
+ (void)register_driver("/dev/null", &devnull_fops, 0666, NULL);
+}
diff --git a/nuttx/drivers/dev_zero.c b/nuttx/drivers/dev_zero.c
new file mode 100644
index 000000000..5435f80ea
--- /dev/null
+++ b/nuttx/drivers/dev_zero.c
@@ -0,0 +1,135 @@
+/****************************************************************************
+ * drivers/dev_null.c
+ *
+ * Copyright (C) 2008-2009, 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <string.h>
+#include <poll.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static ssize_t devzero_read(FAR struct file *, FAR char *, size_t);
+static ssize_t devzero_write(FAR struct file *, FAR const char *, size_t);
+#ifndef CONFIG_DISABLE_POLL
+static int devzero_poll(FAR struct file *filp, FAR struct pollfd *fds,
+ bool setup);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations devzero_fops =
+{
+ 0, /* open */
+ 0, /* close */
+ devzero_read, /* read */
+ devzero_write, /* write */
+ 0, /* seek */
+ 0 /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , devzero_poll /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: devzero_read
+ ****************************************************************************/
+
+static ssize_t devzero_read(FAR struct file *filp, FAR char *buffer, size_t len)
+{
+ memset(buffer, 0, len);
+ return len;
+}
+
+/****************************************************************************
+ * Name: devzero_write
+ ****************************************************************************/
+
+static ssize_t devzero_write(FAR struct file *filp, FAR const char *buffer, size_t len)
+{
+ return len;
+}
+
+/****************************************************************************
+ * Name: devzero_poll
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static int devzero_poll(FAR struct file *filp, FAR struct pollfd *fds,
+ bool setup)
+{
+ if (setup)
+ {
+ fds->revents |= (fds->events & (POLLIN|POLLOUT));
+ if (fds->revents != 0)
+ {
+ sem_post(fds->sem);
+ }
+ }
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: devzero_register
+ *
+ * Description:
+ * Register /dev/zero
+ *
+ ****************************************************************************/
+
+void devzero_register(void)
+{
+ (void)register_driver("/dev/zero", &devzero_fops, 0666, NULL);
+}
diff --git a/nuttx/drivers/input/Kconfig b/nuttx/drivers/input/Kconfig
new file mode 100644
index 000000000..1303cfbd0
--- /dev/null
+++ b/nuttx/drivers/input/Kconfig
@@ -0,0 +1,13 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+config INPUT_TSC2007
+ bool "TI TSC2007 touchscreen controller"
+ default n
+ select I2C
+
+config INPUT_ADS7843E
+ bool "TI ADS7843E touchscreen controller"
+ default n
+ select SPI
diff --git a/nuttx/drivers/input/Make.defs b/nuttx/drivers/input/Make.defs
new file mode 100644
index 000000000..6dbae268e
--- /dev/null
+++ b/nuttx/drivers/input/Make.defs
@@ -0,0 +1,72 @@
+############################################################################
+# drivers/input/Make.defs
+#
+# Copyright (C) 2011 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+# Don't build anything if there is no NX support for input devices
+
+ifeq ($(CONFIG_INPUT),y)
+
+# Include the selected touchscreen drivers
+
+ifeq ($(CONFIG_INPUT_TSC2007),y)
+ CSRCS += tsc2007.c
+endif
+
+ifeq ($(CONFIG_INPUT_ADS7843E),y)
+ CSRCS += ads7843e.c
+endif
+
+ifeq ($(CONFIG_INPUT_STMPE811),y)
+ CSRCS += stmpe811_base.c
+ifneq ($(CONFIG_INPUT_STMPE811_TSC_DISABLE),y)
+ CSRCS += stmpe811_tsc.c
+endif
+ifneq ($(CONFIG_INPUT_STMPE811_GPIO_DISABLE),y)
+ CSRCS += stmpe811_gpio.c
+endif
+ifneq ($(CONFIG_INPUT_STMPE811_ADC_DISABLE),y)
+ CSRCS += stmpe811_adc.c
+endif
+ifneq ($(CONFIG_INPUT_STMPE811_TEMP_DISABLE),y)
+ CSRCS += stmpe811_temp.c
+endif
+endif
+
+# Include input device driver build support
+
+DEPPATH += --dep-path input
+VPATH += :input
+CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/input}
+endif
+
diff --git a/nuttx/drivers/input/ads7843e.c b/nuttx/drivers/input/ads7843e.c
new file mode 100644
index 000000000..e08a7a728
--- /dev/null
+++ b/nuttx/drivers/input/ads7843e.c
@@ -0,0 +1,1199 @@
+/****************************************************************************
+ * drivers/input/ads7843e.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Authors: Gregory Nutt <gnutt@nuttx.org>
+ * Diego Sanchez <dsanchez@nx-engineering.com>
+ *
+ * References:
+ * "Touch Screen Controller, ADS7843," Burr-Brown Products from Texas
+ * Instruments, SBAS090B, September 2000, Revised May 2002"
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <poll.h>
+#include <wdog.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/spi.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/input/touchscreen.h>
+#include <nuttx/input/ads7843e.h>
+
+#include "ads7843e.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* Low-level SPI helpers */
+
+static inline void ads7843e_configspi(FAR struct spi_dev_s *spi);
+#ifdef CONFIG_SPI_OWNBUS
+static inline void ads7843e_select(FAR struct spi_dev_s *spi);
+static inline void ads7843e_deselect(FAR struct spi_dev_s *spi);
+#else
+static void ads7843e_select(FAR struct spi_dev_s *spi);
+static void ads7843e_deselect(FAR struct spi_dev_s *spi);
+#endif
+
+static inline void ads7843e_waitbusy(FAR struct ads7843e_dev_s *priv);
+static uint16_t ads7843e_sendcmd(FAR struct ads7843e_dev_s *priv, uint8_t cmd);
+
+/* Interrupts and data sampling */
+
+static void ads7843e_notify(FAR struct ads7843e_dev_s *priv);
+static int ads7843e_sample(FAR struct ads7843e_dev_s *priv,
+ FAR struct ads7843e_sample_s *sample);
+static int ads7843e_waitsample(FAR struct ads7843e_dev_s *priv,
+ FAR struct ads7843e_sample_s *sample);
+static void ads7843e_worker(FAR void *arg);
+static int ads7843e_interrupt(int irq, FAR void *context);
+
+/* Character driver methods */
+
+static int ads7843e_open(FAR struct file *filep);
+static int ads7843e_close(FAR struct file *filep);
+static ssize_t ads7843e_read(FAR struct file *filep, FAR char *buffer, size_t len);
+static int ads7843e_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+#ifndef CONFIG_DISABLE_POLL
+static int ads7843e_poll(FAR struct file *filep, struct pollfd *fds, bool setup);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This the the vtable that supports the character driver interface */
+
+static const struct file_operations ads7843e_fops =
+{
+ ads7843e_open, /* open */
+ ads7843e_close, /* close */
+ ads7843e_read, /* read */
+ 0, /* write */
+ 0, /* seek */
+ ads7843e_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , ads7843e_poll /* poll */
+#endif
+};
+
+/* If only a single ADS7843E device is supported, then the driver state
+ * structure may as well be pre-allocated.
+ */
+
+#ifndef CONFIG_ADS7843E_MULTIPLE
+static struct ads7843e_dev_s g_ads7843e;
+
+/* Otherwise, we will need to maintain allocated driver instances in a list */
+
+#else
+static struct ads7843e_dev_s *g_ads7843elist;
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: ads7843e_select
+ *
+ * Description:
+ * Select the SPI, locking and re-configuring if necessary. This function
+ * must be called before initiating any sequence of SPI operations. If we
+ * are sharing the SPI bus with other devices (CONFIG_SPI_OWNBUS undefined)
+ * then we need to lock and configure the SPI bus for each transfer.
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void ads7843e_select(FAR struct spi_dev_s *spi)
+{
+ /* We own the SPI bus, so just select the chip */
+
+ SPI_SELECT(spi, SPIDEV_TOUCHSCREEN, true);
+}
+#else
+static void ads7843e_select(FAR struct spi_dev_s *spi)
+{
+ /* Select ADS7843 chip (locking the SPI bus in case there are multiple
+ * devices competing for the SPI bus
+ */
+
+ (void)SPI_LOCK(spi, true);
+ SPI_SELECT(spi, SPIDEV_TOUCHSCREEN, true);
+
+ /* Now make sure that the SPI bus is configured for the ADS7843 (it
+ * might have gotten configured for a different device while unlocked)
+ */
+
+ SPI_SETMODE(spi, CONFIG_ADS7843E_SPIMODE);
+ SPI_SETBITS(spi, 8);
+ SPI_SETFREQUENCY(spi, CONFIG_ADS7843E_FREQUENCY);
+}
+#endif
+
+/****************************************************************************
+ * Function: ads7843e_deselect
+ *
+ * Description:
+ * De-select the SPI, unlocking as necessary. This function must be
+ * after completing a sequence of SPI operations. If we are sharing the SPI
+ * bus with other devices (CONFIG_SPI_OWNBUS undefined) then we need to
+ * un-lock the SPI bus for each transfer, possibly losing the current
+ * configuration.
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void ads7843e_deselect(FAR struct spi_dev_s *spi)
+{
+ /* We own the SPI bus, so just de-select the chip */
+
+ SPI_SELECT(spi, SPIDEV_TOUCHSCREEN, false);
+}
+#else
+static void ads7843e_deselect(FAR struct spi_dev_s *spi)
+{
+ /* De-select ADS7843 chip and relinquish the SPI bus. */
+
+ SPI_SELECT(spi, SPIDEV_TOUCHSCREEN, false);
+ (void)SPI_LOCK(spi, false);
+}
+#endif
+
+/****************************************************************************
+ * Function: ads7843e_configspi
+ *
+ * Description:
+ * Configure the SPI for use with the ADS7843E. This function should be
+ * called once during touchscreen initialization to configure the SPI
+ * bus. Note that if CONFIG_SPI_OWNBUS is not defined, then this function
+ * does nothing.
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void ads7843e_configspi(FAR struct spi_dev_s *spi)
+{
+ idbg("Mode: %d Bits: 8 Frequency: %d\n",
+ CONFIG_ADS7843E_SPIMODE, CONFIG_ADS7843E_FREQUENCY);
+
+ /* Configure SPI for the ADS7843. But only if we own the SPI bus. Otherwise, don't
+ * bother because it might change.
+ */
+
+#ifdef CONFIG_SPI_OWNBUS
+ SPI_SELECT(spi, SPIDEV_TOUCHSCREEN, true);
+ SPI_SETMODE(spi, CONFIG_ADS7843E_SPIMODE);
+ SPI_SETBITS(spi, 8);
+ SPI_SETFREQUENCY(spi, CONFIG_ADS7843E_FREQUENCY);
+ SPI_SELECT(spi, SPIDEV_TOUCHSCREEN, false);
+#endif
+}
+
+/****************************************************************************
+ * Name: ads7843e_waitbusy
+ ****************************************************************************/
+
+static inline void ads7843e_waitbusy(FAR struct ads7843e_dev_s *priv)
+{
+ while (priv->config->busy(priv->config));
+}
+
+/****************************************************************************
+ * Name: ads7843e_sendcmd
+ ****************************************************************************/
+
+static uint16_t ads7843e_sendcmd(FAR struct ads7843e_dev_s *priv, uint8_t cmd)
+{
+ uint8_t buffer[2];
+ uint16_t result;
+
+ /* Select the ADS7843E */
+
+ ads7843e_select(priv->spi);
+
+ /* Send the command */
+
+ (void)SPI_SEND(priv->spi, cmd);
+ ads7843e_waitbusy(priv);
+
+ /* Read the data */
+
+ SPI_RECVBLOCK(priv->spi, buffer, 2);
+ ads7843e_deselect(priv->spi);
+
+ result = ((uint16_t)buffer[0] << 8) | (uint16_t)buffer[1];
+ result = result >> 4;
+
+ ivdbg("cmd:%02x response:%04x\n", cmd, result);
+ return result;
+}
+
+/****************************************************************************
+ * Name: ads7843e_notify
+ ****************************************************************************/
+
+static void ads7843e_notify(FAR struct ads7843e_dev_s *priv)
+{
+#ifndef CONFIG_DISABLE_POLL
+ int i;
+#endif
+
+ /* If there are threads waiting for read data, then signal one of them
+ * that the read data is available.
+ */
+
+ if (priv->nwaiters > 0)
+ {
+ /* After posting this semaphore, we need to exit because the ADS7843E
+ * is no longer available.
+ */
+
+ sem_post(&priv->waitsem);
+ }
+
+ /* If there are threads waiting on poll() for ADS7843E data to become available,
+ * then wake them up now. NOTE: we wake up all waiting threads because we
+ * do not know that they are going to do. If they all try to read the data,
+ * then some make end up blocking after all.
+ */
+
+#ifndef CONFIG_DISABLE_POLL
+ for (i = 0; i < CONFIG_ADS7843E_NPOLLWAITERS; i++)
+ {
+ struct pollfd *fds = priv->fds[i];
+ if (fds)
+ {
+ fds->revents |= POLLIN;
+ ivdbg("Report events: %02x\n", fds->revents);
+ sem_post(fds->sem);
+ }
+ }
+#endif
+}
+
+/****************************************************************************
+ * Name: ads7843e_sample
+ ****************************************************************************/
+
+static int ads7843e_sample(FAR struct ads7843e_dev_s *priv,
+ FAR struct ads7843e_sample_s *sample)
+{
+ irqstate_t flags;
+ int ret = -EAGAIN;
+
+ /* Interrupts me be disabled when this is called to (1) prevent posting
+ * of semaphores from interrupt handlers, and (2) to prevent sampled data
+ * from changing until it has been reported.
+ */
+
+ flags = irqsave();
+
+ /* Is there new ADS7843E sample data available? */
+
+ if (priv->penchange)
+ {
+ /* Yes.. the state has changed in some way. Return a copy of the
+ * sampled data.
+ */
+
+ memcpy(sample, &priv->sample, sizeof(struct ads7843e_sample_s ));
+
+ /* Now manage state transitions */
+
+ if (sample->contact == CONTACT_UP)
+ {
+ /* Next.. no contact. Increment the ID so that next contact ID
+ * will be unique. X/Y positions are no longer valid.
+ */
+
+ priv->sample.contact = CONTACT_NONE;
+ priv->sample.valid = false;
+ priv->id++;
+ }
+ else if (sample->contact == CONTACT_DOWN)
+ {
+ /* First report -- next report will be a movement */
+
+ priv->sample.contact = CONTACT_MOVE;
+ }
+
+ priv->penchange = false;
+ ret = OK;
+ }
+
+ irqrestore(flags);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: ads7843e_waitsample
+ ****************************************************************************/
+
+static int ads7843e_waitsample(FAR struct ads7843e_dev_s *priv,
+ FAR struct ads7843e_sample_s *sample)
+{
+ irqstate_t flags;
+ int ret;
+
+ /* Interrupts me be disabled when this is called to (1) prevent posting
+ * of semaphores from interrupt handlers, and (2) to prevent sampled data
+ * from changing until it has been reported.
+ *
+ * In addition, we will also disable pre-emption to prevent other threads
+ * from getting control while we muck with the semaphores.
+ */
+
+ sched_lock();
+ flags = irqsave();
+
+ /* Now release the semaphore that manages mutually exclusive access to
+ * the device structure. This may cause other tasks to become ready to
+ * run, but they cannot run yet because pre-emption is disabled.
+ */
+
+ sem_post(&priv->devsem);
+
+ /* Try to get the a sample... if we cannot, then wait on the semaphore
+ * that is posted when new sample data is available.
+ */
+
+ while (ads7843e_sample(priv, sample) < 0)
+ {
+ /* Wait for a change in the ADS7843E state */
+
+ ivdbg("Waiting..\n");
+ priv->nwaiters++;
+ ret = sem_wait(&priv->waitsem);
+ priv->nwaiters--;
+
+ if (ret < 0)
+ {
+ /* If we are awakened by a signal, then we need to return
+ * the failure now.
+ */
+
+ idbg("sem_wait: %d\n", errno);
+ DEBUGASSERT(errno == EINTR);
+ ret = -EINTR;
+ goto errout;
+ }
+ }
+
+ ivdbg("Sampled\n");
+
+ /* Re-acquire the the semaphore that manages mutually exclusive access to
+ * the device structure. We may have to wait here. But we have our sample.
+ * Interrupts and pre-emption will be re-enabled while we wait.
+ */
+
+ ret = sem_wait(&priv->devsem);
+
+errout:
+ /* Then re-enable interrupts. We might get interrupt here and there
+ * could be a new sample. But no new threads will run because we still
+ * have pre-emption disabled.
+ */
+
+ irqrestore(flags);
+
+ /* Restore pre-emption. We might get suspended here but that is okay
+ * because we already have our sample. Note: this means that if there
+ * were two threads reading from the ADS7843E for some reason, the data
+ * might be read out of order.
+ */
+
+ sched_unlock();
+ return ret;
+}
+
+/****************************************************************************
+ * Name: ads7843e_schedule
+ ****************************************************************************/
+
+static int ads7843e_schedule(FAR struct ads7843e_dev_s *priv)
+{
+ FAR struct ads7843e_config_s *config;
+ int ret;
+
+ /* Get a pointer the callbacks for convenience (and so the code is not so
+ * ugly).
+ */
+
+ config = priv->config;
+ DEBUGASSERT(config != NULL);
+
+ /* Disable further interrupts. ADS7843E interrupts will be re-enabled
+ * after the worker thread executes.
+ */
+
+ config->enable(config, false);
+
+ /* Disable the watchdog timer. It will be re-enabled in the worker thread
+ * while the pen remains down.
+ */
+
+ wd_cancel(priv->wdog);
+
+ /* Transfer processing to the worker thread. Since ADS7843E interrupts are
+ * disabled while the work is pending, no special action should be required
+ * to protected the work queue.
+ */
+
+ DEBUGASSERT(priv->work.worker == NULL);
+ ret = work_queue(HPWORK, &priv->work, ads7843e_worker, priv, 0);
+ if (ret != 0)
+ {
+ illdbg("Failed to queue work: %d\n", ret);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: ads7843e_wdog
+ ****************************************************************************/
+
+static void ads7843e_wdog(int argc, uint32_t arg1, ...)
+{
+ FAR struct ads7843e_dev_s *priv = (FAR struct ads7843e_dev_s *)((uintptr_t)arg1);
+ (void)ads7843e_schedule(priv);
+}
+
+/****************************************************************************
+ * Name: ads7843e_worker
+ ****************************************************************************/
+
+static void ads7843e_worker(FAR void *arg)
+{
+ FAR struct ads7843e_dev_s *priv = (FAR struct ads7843e_dev_s *)arg;
+ FAR struct ads7843e_config_s *config;
+ bool pendown;
+
+ ASSERT(priv != NULL);
+
+ /* Get a pointer the callbacks for convenience (and so the code is not so
+ * ugly).
+ */
+
+ config = priv->config;
+ DEBUGASSERT(config != NULL);
+
+ /* Disable the watchdog timer */
+
+ wd_cancel(priv->wdog);
+
+ /* Check for pen up or down by reading the PENIRQ GPIO. */
+
+ pendown = config->pendown(config);
+
+ /* Handle the change from pen down to pen up */
+
+ if (!pendown)
+ {
+ /* Ignore the interrupt if the pen was already up (CONTACT_NONE == pen up and
+ * already reported. CONTACT_UP == pen up, but not reported)
+ */
+
+ if (priv->sample.contact == CONTACT_NONE)
+ {
+ goto errout;
+ }
+
+ /* The pen is up. NOTE: We know from a previous test, that this is a
+ * loss of contact condition. This will be changed to CONTACT_NONE
+ * after the loss of contact is sampled.
+ */
+
+ priv->sample.contact = CONTACT_UP;
+ }
+
+ /* It is a pen down event. If the last loss-of-contact event has not been
+ * processed yet, then we have to ignore the pen down event (or else it will
+ * look like a drag event)
+ */
+
+ else if (priv->sample.contact == CONTACT_UP)
+ {
+ goto errout;
+ }
+ else
+ {
+ /* Handle pen down events. First, sample positional values. */
+
+ priv->sample.x = ads7843e_sendcmd(priv, ADS7843_CMD_XPOSITION);
+ priv->sample.y = ads7843e_sendcmd(priv, ADS7843_CMD_YPOSITION);
+
+ /* The X/Y positional data is now valid */
+
+ priv->sample.valid = true;
+
+ /* If this is the first (acknowledged) pen down report, then report
+ * this as the first contact. If contact == CONTACT_DOWN, it will be
+ * set to set to CONTACT_MOVE after the contact is first sampled.
+ */
+
+ if (priv->sample.contact != CONTACT_MOVE)
+ {
+ /* First contact */
+
+ priv->sample.contact = CONTACT_DOWN;
+ }
+
+ /* Continue to sample the position while the pen is down */
+
+ wd_start(priv->wdog, ADS7843E_WDOG_DELAY, ads7843e_wdog, 1, (uint32_t)priv);
+ }
+
+ /* Indicate the availability of new sample data for this ID */
+
+ priv->sample.id = priv->id;
+ priv->penchange = true;
+
+ /* Notify any waiters that new ADS7843E data is available */
+
+ ads7843e_notify(priv);
+
+ /* Exit, re-enabling ADS7843E interrupts */
+
+errout:
+ (void)ads7843e_sendcmd(priv, ADS7843_CMD_ENABPINIRQ);
+ config->enable(config, true);
+}
+
+/****************************************************************************
+ * Name: ads7843e_interrupt
+ ****************************************************************************/
+
+static int ads7843e_interrupt(int irq, FAR void *context)
+{
+ FAR struct ads7843e_dev_s *priv;
+ FAR struct ads7843e_config_s *config;
+ int ret;
+
+ /* Which ADS7843E device caused the interrupt? */
+
+#ifndef CONFIG_ADS7843E_MULTIPLE
+ priv = &g_ads7843e;
+#else
+ for (priv = g_ads7843elist;
+ priv && priv->configs->irq != irq;
+ priv = priv->flink);
+
+ ASSERT(priv != NULL);
+#endif
+
+ /* Get a pointer the callbacks for convenience (and so the code is not so
+ * ugly).
+ */
+
+ config = priv->config;
+ DEBUGASSERT(config != NULL);
+
+ /* Schedule sampling to occur on the worker thread */
+
+ ret = ads7843e_schedule(priv);
+
+ /* Clear any pending interrupts and return success */
+
+ config->clear(config);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: ads7843e_open
+ ****************************************************************************/
+
+static int ads7843e_open(FAR struct file *filep)
+{
+#ifdef CONFIG_ADS7843E_REFCNT
+ FAR struct inode *inode;
+ FAR struct ads7843e_dev_s *priv;
+ uint8_t tmp;
+ int ret;
+
+ ivdbg("Opening\n");
+
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct ads7843e_dev_s *)inode->i_private;
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->devsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Increment the reference count */
+
+ tmp = priv->crefs + 1;
+ if (tmp == 0)
+ {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ goto errout_with_sem;
+ }
+
+ /* When the reference increments to 1, this is the first open event
+ * on the driver.. and an opportunity to do any one-time initialization.
+ */
+
+ /* Save the new open count on success */
+
+ priv->crefs = tmp;
+
+errout_with_sem:
+ sem_post(&priv->devsem);
+ return ret;
+#else
+ ivdbg("Opening\n");
+ return OK;
+#endif
+}
+
+/****************************************************************************
+ * Name: ads7843e_close
+ ****************************************************************************/
+
+static int ads7843e_close(FAR struct file *filep)
+{
+#ifdef CONFIG_ADS7843E_REFCNT
+ FAR struct inode *inode;
+ FAR struct ads7843e_dev_s *priv;
+ int ret;
+
+ ivdbg("Closing\n");
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct ads7843e_dev_s *)inode->i_private;
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->devsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Decrement the reference count unless it would decrement a negative
+ * value. When the count decrements to zero, there are no further
+ * open references to the driver.
+ */
+
+ if (priv->crefs >= 1)
+ {
+ priv->crefs--;
+ }
+
+ sem_post(&priv->devsem);
+#endif
+ ivdbg("Closing\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: ads7843e_read
+ ****************************************************************************/
+
+static ssize_t ads7843e_read(FAR struct file *filep, FAR char *buffer, size_t len)
+{
+ FAR struct inode *inode;
+ FAR struct ads7843e_dev_s *priv;
+ FAR struct touch_sample_s *report;
+ struct ads7843e_sample_s sample;
+ int ret;
+
+ ivdbg("buffer:%p len:%d\n", buffer, len);
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct ads7843e_dev_s *)inode->i_private;
+
+ /* Verify that the caller has provided a buffer large enough to receive
+ * the touch data.
+ */
+
+ if (len < SIZEOF_TOUCH_SAMPLE_S(1))
+ {
+ /* We could provide logic to break up a touch report into segments and
+ * handle smaller reads... but why?
+ */
+
+ idbg("Unsupported read size: %d\n", len);
+ return -ENOSYS;
+ }
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->devsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ idbg("sem_wait: %d\n", errno);
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Try to read sample data. */
+
+ ret = ads7843e_sample(priv, &sample);
+ if (ret < 0)
+ {
+ /* Sample data is not available now. We would ave to wait to get
+ * receive sample data. If the user has specified the O_NONBLOCK
+ * option, then just return an error.
+ */
+
+ ivdbg("Sample data is not available\n");
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ ret = -EAGAIN;
+ goto errout;
+ }
+
+ /* Wait for sample data */
+
+ ret = ads7843e_waitsample(priv, &sample);
+ if (ret < 0)
+ {
+ /* We might have been awakened by a signal */
+
+ idbg("ads7843e_waitsample: %d\n", ret);
+ goto errout;
+ }
+ }
+
+ /* In any event, we now have sampled ADS7843E data that we can report
+ * to the caller.
+ */
+
+ report = (FAR struct touch_sample_s *)buffer;
+ memset(report, 0, SIZEOF_TOUCH_SAMPLE_S(1));
+ report->npoints = 1;
+ report->point[0].id = priv->id;
+ report->point[0].x = sample.x;
+ report->point[0].y = sample.y;
+
+ /* Report the appropriate flags */
+
+ if (sample.contact == CONTACT_UP)
+ {
+ /* Pen is now up. Is the positional data valid? This is important to
+ * know because the release will be sent to the window based on its
+ * last positional data.
+ */
+
+ if (sample.valid)
+ {
+ report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID |
+ TOUCH_POS_VALID | TOUCH_PRESSURE_VALID;
+ }
+ else
+ {
+ report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID;
+ }
+ }
+ else if (sample.contact == CONTACT_DOWN)
+ {
+ /* First contact */
+
+ report->point[0].flags = TOUCH_DOWN | TOUCH_ID_VALID | TOUCH_POS_VALID;
+ }
+ else /* if (sample->contact == CONTACT_MOVE) */
+ {
+ /* Movement of the same contact */
+
+ report->point[0].flags = TOUCH_MOVE | TOUCH_ID_VALID | TOUCH_POS_VALID;
+ }
+
+ ivdbg(" id: %d\n", report->point[0].id);
+ ivdbg(" flags: %02x\n", report->point[0].flags);
+ ivdbg(" x: %d\n", report->point[0].x);
+ ivdbg(" y: %d\n", report->point[0].y);
+
+ ret = SIZEOF_TOUCH_SAMPLE_S(1);
+
+errout:
+ sem_post(&priv->devsem);
+ ivdbg("Returning: %d\n", ret);
+ return ret;
+}
+
+/****************************************************************************
+ * Name:ads7843e_ioctl
+ ****************************************************************************/
+
+static int ads7843e_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode;
+ FAR struct ads7843e_dev_s *priv;
+ int ret;
+
+ ivdbg("cmd: %d arg: %ld\n", cmd, arg);
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct ads7843e_dev_s *)inode->i_private;
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->devsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Process the IOCTL by command */
+
+ switch (cmd)
+ {
+ case TSIOC_SETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
+ {
+ FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
+ DEBUGASSERT(priv->config != NULL && ptr != NULL);
+ priv->config->frequency = SPI_SETFREQUENCY(priv->spi, *ptr);
+ }
+ break;
+
+ case TSIOC_GETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
+ {
+ FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
+ DEBUGASSERT(priv->config != NULL && ptr != NULL);
+ *ptr = priv->config->frequency;
+ }
+ break;
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ sem_post(&priv->devsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: ads7843e_poll
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static int ads7843e_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup)
+{
+ FAR struct inode *inode;
+ FAR struct ads7843e_dev_s *priv;
+ pollevent_t eventset;
+ int ndx;
+ int ret = OK;
+ int i;
+
+ ivdbg("setup: %d\n", (int)setup);
+ DEBUGASSERT(filep && fds);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct ads7843e_dev_s *)inode->i_private;
+
+ /* Are we setting up the poll? Or tearing it down? */
+
+ ret = sem_wait(&priv->devsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ if (setup)
+ {
+ /* Ignore waits that do not include POLLIN */
+
+ if ((fds->events & POLLIN) == 0)
+ {
+ ret = -EDEADLK;
+ goto errout;
+ }
+
+ /* This is a request to set up the poll. Find an available
+ * slot for the poll structure reference
+ */
+
+ for (i = 0; i < CONFIG_ADS7843E_NPOLLWAITERS; i++)
+ {
+ /* Find an available slot */
+
+ if (!priv->fds[i])
+ {
+ /* Bind the poll structure and this slot */
+
+ priv->fds[i] = fds;
+ fds->priv = &priv->fds[i];
+ break;
+ }
+ }
+
+ if (i >= CONFIG_ADS7843E_NPOLLWAITERS)
+ {
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto errout;
+ }
+
+ /* Should we immediately notify on any of the requested events? */
+
+ if (priv->penchange)
+ {
+ ads7843e_notify(priv);
+ }
+ }
+ else if (fds->priv)
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+ DEBUGASSERT(slot != NULL);
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+errout:
+ sem_post(&priv->devsem);
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ads7843e_register
+ *
+ * Description:
+ * Configure the ADS7843E to use the provided SPI device instance. This
+ * will register the driver as /dev/inputN where N is the minor device
+ * number
+ *
+ * Input Parameters:
+ * dev - An SPI driver instance
+ * config - Persistent board configuration data
+ * minor - The input device minor number
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int ads7843e_register(FAR struct spi_dev_s *dev,
+ FAR struct ads7843e_config_s *config, int minor)
+{
+ FAR struct ads7843e_dev_s *priv;
+ char devname[DEV_NAMELEN];
+#ifdef CONFIG_ADS7843E_MULTIPLE
+ irqstate_t flags;
+#endif
+ int ret;
+
+ ivdbg("dev: %p minor: %d\n", dev, minor);
+
+ /* Debug-only sanity checks */
+
+ DEBUGASSERT(dev != NULL && config != NULL && minor >= 0 && minor < 100);
+
+ /* Create and initialize a ADS7843E device driver instance */
+
+#ifndef CONFIG_ADS7843E_MULTIPLE
+ priv = &g_ads7843e;
+#else
+ priv = (FAR struct ads7843e_dev_s *)kmalloc(sizeof(struct ads7843e_dev_s));
+ if (!priv)
+ {
+ idbg("kmalloc(%d) failed\n", sizeof(struct ads7843e_dev_s));
+ return -ENOMEM;
+ }
+#endif
+
+ /* Initialize the ADS7843E device driver instance */
+
+ memset(priv, 0, sizeof(struct ads7843e_dev_s));
+ priv->spi = dev; /* Save the SPI device handle */
+ priv->config = config; /* Save the board configuration */
+ priv->wdog = wd_create(); /* Create a watchdog timer */
+ sem_init(&priv->devsem, 0, 1); /* Initialize device structure semaphore */
+ sem_init(&priv->waitsem, 0, 0); /* Initialize pen event wait semaphore */
+
+ /* Make sure that interrupts are disabled */
+
+ config->clear(config);
+ config->enable(config, false);
+
+ /* Attach the interrupt handler */
+
+ ret = config->attach(config, ads7843e_interrupt);
+ if (ret < 0)
+ {
+ idbg("Failed to attach interrupt\n");
+ goto errout_with_priv;
+ }
+
+ /* Configure the SPI interface */
+
+ ads7843e_configspi(dev);
+
+ /* Enable the PEN IRQ */
+
+ ads7843e_sendcmd(priv, ADS7843_CMD_ENABPINIRQ);
+
+ /* Register the device as an input device */
+
+ (void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, minor);
+ ivdbg("Registering %s\n", devname);
+
+ ret = register_driver(devname, &ads7843e_fops, 0666, priv);
+ if (ret < 0)
+ {
+ idbg("register_driver() failed: %d\n", ret);
+ goto errout_with_priv;
+ }
+
+ /* If multiple ADS7843E devices are supported, then we will need to add
+ * this new instance to a list of device instances so that it can be
+ * found by the interrupt handler based on the recieved IRQ number.
+ */
+
+#ifdef CONFIG_ADS7843E_MULTIPLE
+ priv->flink = g_ads7843elist;
+ g_ads7843elist = priv;
+ irqrestore(flags);
+#endif
+
+ /* Schedule work to perform the initial sampling and to set the data
+ * availability conditions.
+ */
+
+ ret = work_queue(HPWORK, &priv->work, ads7843e_worker, priv, 0);
+ if (ret != 0)
+ {
+ idbg("Failed to queue work: %d\n", ret);
+ goto errout_with_priv;
+ }
+
+ /* And return success (?) */
+
+ return OK;
+
+errout_with_priv:
+ sem_destroy(&priv->devsem);
+#ifdef CONFIG_ADS7843E_MULTIPLE
+ kfree(priv);
+#endif
+ return ret;
+}
diff --git a/nuttx/drivers/input/ads7843e.h b/nuttx/drivers/input/ads7843e.h
new file mode 100644
index 000000000..030d1cb33
--- /dev/null
+++ b/nuttx/drivers/input/ads7843e.h
@@ -0,0 +1,171 @@
+/********************************************************************************************
+ * drivers/input/ads7843e.h
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "Touch Screen Controller, ADS7843," Burr-Brown Products from Texas
+ * Instruments, SBAS090B, September 2000, Revised May 2002"
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ********************************************************************************************/
+
+#ifndef __DRIVERS_INPUT_ADS7843E_H
+#define __DRIVERS_INPUT_ADS7843E_H
+
+/********************************************************************************************
+ * Included Files
+ ********************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <semaphore.h>
+#include <poll.h>
+#include <wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/spi.h>
+#include <nuttx/clock.h>
+#include <nuttx/input/ads7843e.h>
+
+/********************************************************************************************
+ * Pre-Processor Definitions
+ ********************************************************************************************/
+/* Configuration ****************************************************************************/
+/* Reference counting is partially implemented, but not needed in the current design. */
+
+#undef CONFIG_ADS7843E_REFCNT
+
+/* ADS7843E Interfaces *********************************************************************/
+/* ADS7843E command bit settings */
+
+#define ADS7843E_CMD_PD0 (1 << 0) /* PD0 */
+#define ADS7843E_CMD_PD1 (1 << 1) /* PD1 */
+#define ADS7843E_CMD_DFR (1 << 2) /* SER/DFR */
+#define ADS7843E_CMD_EIGHT_BITS_MOD (1 << 3) /* Mode */
+#define ADS7843E_CMD_START (1 << 7) /* Start Bit */
+#define ADS7843E_CMD_SWITCH_SHIFT 4 /* Address setting */
+
+/* ADS7843E Commands */
+
+#define ADS7843_CMD_YPOSITION \
+ ((1 << ADS7843E_CMD_SWITCH_SHIFT)|ADS7843E_CMD_START|ADS7843E_CMD_PD0|ADS7843E_CMD_PD1)
+#define ADS7843_CMD_XPOSITION \
+ ((5 << ADS7843E_CMD_SWITCH_SHIFT)|ADS7843E_CMD_START|ADS7843E_CMD_PD0|ADS7843E_CMD_PD1)
+#define ADS7843_CMD_ENABPINIRQ \
+ ((1 << ADS7843E_CMD_SWITCH_SHIFT)|ADS7843E_CMD_START)
+
+/* Driver support **************************************************************************/
+/* This format is used to construct the /dev/input[n] device driver path. It
+ * defined here so that it will be used consistently in all places.
+ */
+
+#define DEV_FORMAT "/dev/input%d"
+#define DEV_NAMELEN 16
+
+/* Poll the pen position while the pen is down at this rate (50MS): */
+
+#define ADS7843E_WDOG_DELAY ((50 + (MSEC_PER_TICK-1))/ MSEC_PER_TICK)
+
+/********************************************************************************************
+ * Public Types
+ ********************************************************************************************/
+
+/* This describes the state of one contact */
+
+enum ads7843e_contact_3
+{
+ CONTACT_NONE = 0, /* No contact */
+ CONTACT_DOWN, /* First contact */
+ CONTACT_MOVE, /* Same contact, possibly different position */
+ CONTACT_UP, /* Contact lost */
+};
+
+/* This structure describes the results of one ADS7843E sample */
+
+struct ads7843e_sample_s
+{
+ uint8_t id; /* Sampled touch point ID */
+ uint8_t contact; /* Contact state (see enum ads7843e_contact_e) */
+ bool valid; /* True: x,y contain valid, sampled data */
+ uint16_t x; /* Measured X position */
+ uint16_t y; /* Measured Y position */
+};
+
+/* This structure describes the state of one ADS7843E driver instance */
+
+struct ads7843e_dev_s
+{
+#ifdef CONFIG_ADS7843E_MULTIPLE
+ FAR struct ads7843e_dev_s *flink; /* Supports a singly linked list of drivers */
+#endif
+#ifdef CONFIG_ADS7843E_REFCNT
+ uint8_t crefs; /* Number of times the device has been opened */
+#endif
+ uint8_t nwaiters; /* Number of threads waiting for ADS7843E data */
+ uint8_t id; /* Current touch point ID */
+ volatile bool penchange; /* An unreported event is buffered */
+ sem_t devsem; /* Manages exclusive access to this structure */
+ sem_t waitsem; /* Used to wait for the availability of data */
+
+ FAR struct ads7843e_config_s *config; /* Board configuration data */
+ FAR struct spi_dev_s *spi; /* Saved SPI driver instance */
+ struct work_s work; /* Supports the interrupt handling "bottom half" */
+ struct ads7843e_sample_s sample; /* Last sampled touch point data */
+ WDOG_ID wdog; /* Poll the position while the pen is down */
+
+ /* The following is a list if poll structures of threads waiting for
+ * driver events. The 'struct pollfd' reference for each open is also
+ * retained in the f_priv field of the 'struct file'.
+ */
+
+#ifndef CONFIG_DISABLE_POLL
+ struct pollfd *fds[CONFIG_ADS7843E_NPOLLWAITERS];
+#endif
+};
+
+/********************************************************************************************
+ * Public Function Prototypes
+ ********************************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRIVERS_INPUT_ADS7843E_H */
diff --git a/nuttx/drivers/input/stmpe811.h b/nuttx/drivers/input/stmpe811.h
new file mode 100644
index 000000000..f6d646527
--- /dev/null
+++ b/nuttx/drivers/input/stmpe811.h
@@ -0,0 +1,245 @@
+/********************************************************************************************
+ * drivers/input/stmpe811.h
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "STMPE811 S-Touch® advanced resistive touchscreen controller with 8-bit
+ * GPIO expander," Doc ID 14489 Rev 6, CD00186725, STMicroelectronics"
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ********************************************************************************************/
+
+#ifndef __DRIVERS_INPUT_STMPE811_H
+#define __DRIVERS_INPUT_STMPE811_H
+
+/********************************************************************************************
+ * Included Files
+ ********************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <wdog.h>
+#include <semaphore.h>
+
+#include <nuttx/clock.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/input/stmpe811.h>
+
+#if defined(CONFIG_INPUT) && defined(CONFIG_INPUT_STMPE811)
+
+/********************************************************************************************
+ * Pre-Processor Definitions
+ ********************************************************************************************/
+/* Configuration ****************************************************************************/
+/* Reference counting is partially implemented, but not needed in the current design.
+ */
+
+#undef CONFIG_STMPE811_REFCNT
+
+/* No support for the SPI interface yet */
+
+#ifdef CONFIG_STMPE811_SPI
+# error "Only the STMPE811 I2C interface is supported by this driver"
+#endif
+
+/* Driver support ***************************************************************************/
+/* This format is used to construct the /dev/input[n] device driver path. It defined here
+ * so that it will be used consistently in all places.
+ */
+
+#define DEV_FORMAT "/dev/input%d"
+#define DEV_NAMELEN 16
+
+/* STMPE811 Resources ************************************************************************/
+#ifndef CONFIG_STMPE811_TSC_DISABLE
+# define STMPE811_ADC_NPINS 4 /* Only pins 0-3 can be used for ADC */
+# define STMPE811_GPIO_NPINS 4 /* Only pins 0-3 can be used as GPIOs */
+#else
+# define STMPE811_ADC_NPINS 8 /* All pins can be used for ADC */
+# define STMPE811_GPIO_NPINS 8 /* All pins can be used as GPIOs */
+#endif
+
+/* Driver flags */
+
+#define STMPE811_FLAGS_TSC_INITIALIZED (1 << 0) /* 1: The TSC block has been initialized */
+#define STMPE811_FLAGS_GPIO_INITIALIZED (1 << 1) /* 1: The GIO block has been initialized */
+#define STMPE811_FLAGS_ADC_INITIALIZED (1 << 2) /* 1: The ADC block has been initialized */
+#define STMPE811_FLAGS_TS_INITIALIZED (1 << 3) /* 1: The TS block has been initialized */
+
+/* Timeout to detect missing pen up events */
+
+#define STMPE811_PENUP_TICKS ((100 + (MSEC_PER_TICK-1)) / MSEC_PER_TICK)
+
+/********************************************************************************************
+ * Public Types
+ ********************************************************************************************/
+/* This describes the state of one contact */
+
+enum stmpe811_contact_3
+{
+ CONTACT_NONE = 0, /* No contact */
+ CONTACT_DOWN, /* First contact */
+ CONTACT_MOVE, /* Same contact, possibly different position */
+ CONTACT_UP, /* Contact lost */
+};
+
+/* This structure describes the results of one STMPE811 sample */
+
+struct stmpe811_sample_s
+{
+ uint8_t id; /* Sampled touch point ID */
+ uint8_t contact; /* Contact state (see enum stmpe811_contact_e) */
+ bool valid; /* True: x,y,z contain valid, sampled data */
+ uint16_t x; /* Measured X position */
+ uint16_t y; /* Measured Y position */
+ uint8_t z; /* Measured Z index */
+};
+
+/* This structure represents the state of the STMPE811 driver */
+
+struct stmpe811_dev_s
+{
+#ifdef CONFIG_STMPE811_MULTIPLE
+ FAR struct stmpe811_dev_s *flink; /* Supports a singly linked list of drivers */
+#endif
+
+ /* Common fields */
+
+ FAR struct stmpe811_config_s *config; /* Board configuration data */
+ sem_t exclsem; /* Manages exclusive access to this structure */
+#ifdef CONFIG_STMPE811_SPI
+ FAR struct spi_dev_s *spi; /* Saved SPI driver instance */
+#else
+ FAR struct i2c_dev_s *i2c; /* Saved I2C driver instance */
+#endif
+
+ uint8_t inuse; /* STMPE811 pins in use */
+ uint8_t flags; /* See STMPE811_FLAGS_* definitions */
+ struct work_s work; /* Supports the interrupt handling "bottom half" */
+
+ /* Fields that may be disabled to save size if touchscreen support is not used. */
+
+#ifndef CONFIG_STMPE811_TSC_DISABLE
+#ifdef CONFIG_STMPE811_REFCNT
+ uint8_t crefs; /* Number of times the device has been opened */
+#endif
+ uint8_t nwaiters; /* Number of threads waiting for STMPE811 data */
+ uint8_t id; /* Current touch point ID */
+ uint8_t minor; /* Touchscreen minor device number */
+ volatile bool penchange; /* An unreported event is buffered */
+
+ uint16_t threshx; /* Thresholded X value */
+ uint16_t threshy; /* Thresholded Y value */
+ sem_t waitsem; /* Used to wait for the availability of data */
+
+ struct work_s timeout; /* Supports tiemeout work */
+ WDOG_ID wdog; /* Timeout to detect missing pen down events */
+ struct stmpe811_sample_s sample; /* Last sampled touch point data */
+
+ /* The following is a list if poll structures of threads waiting for
+ * driver events. The 'struct pollfd' reference for each open is also
+ * retained in the f_priv field of the 'struct file'.
+ */
+
+#ifndef CONFIG_DISABLE_POLL
+ struct pollfd *fds[CONFIG_STMPE811_NPOLLWAITERS];
+#endif
+#endif
+
+ /* Fields that may be disabled to save size of GPIO support is not used */
+
+#if !defined(CONFIG_STMPE811_GPIO_DISABLE) && !defined(CONFIG_STMPE811_GPIOINT_DISABLE)
+ stmpe811_handler_t handlers[STMPE811_GPIO_NPINS]; /* GPIO "interrupt handlers" */
+#endif
+};
+
+/********************************************************************************************
+ * Public Function Prototypes
+ ********************************************************************************************/
+
+/********************************************************************************************
+ * Name: stmpe811_getreg8
+ *
+ * Description:
+ * Read from an 8-bit STMPE811 register
+ *
+ ********************************************************************************************/
+
+uint8_t stmpe811_getreg8(FAR struct stmpe811_dev_s *priv, uint8_t regaddr);
+
+/********************************************************************************************
+ * Name: stmpe811_putreg8
+ *
+ * Description:
+ * Write a value to an 8-bit STMPE811 register
+ *
+ ********************************************************************************************/
+
+void stmpe811_putreg8(FAR struct stmpe811_dev_s *priv, uint8_t regaddr, uint8_t regval);
+
+/********************************************************************************************
+ * Name: stmpe811_getreg16
+ *
+ * Description:
+ * Read 16-bits of data from an STMPE-11 register
+ *
+ ********************************************************************************************/
+
+uint16_t stmpe811_getreg16(FAR struct stmpe811_dev_s *priv, uint8_t regaddr);
+
+/********************************************************************************************
+ * Name: stmpe811_tscint
+ *
+ * Description:
+ * Handle touchscreen interrupt events (this function actually executes in the context of
+ * the worker thread).
+ *
+ ********************************************************************************************/
+
+#ifndef CONFIG_STMPE811_TSC_DISABLE
+void stmpe811_tscworker(FAR struct stmpe811_dev_s *priv, uint8_t intsta) weak_function;
+#endif
+
+/********************************************************************************************
+ * Name: stmpe811_gpioworker
+ *
+ * Description:
+ * Handle GPIO interrupt events (this function actually executes in the context of the
+ * worker thread).
+ *
+ ********************************************************************************************/
+
+#if !defined(CONFIG_STMPE811_GPIO_DISABLE) && !defined(CONFIG_STMPE811_GPIOINT_DISABLE)
+void stmpe811_gpioworker(FAR struct stmpe811_dev_s *priv) weak_function;
+#endif
+
+#endif /* CONFIG_INPUT && CONFIG_INPUT_STMPE811 */
+#endif /* __DRIVERS_INPUT_STMPE811_H */
diff --git a/nuttx/drivers/input/stmpe811_adc.c b/nuttx/drivers/input/stmpe811_adc.c
new file mode 100644
index 000000000..1ffe987e6
--- /dev/null
+++ b/nuttx/drivers/input/stmpe811_adc.c
@@ -0,0 +1,266 @@
+/****************************************************************************
+ * drivers/input/stmpe811_adc.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "STMPE811 S-Touch® advanced resistive touchscreen controller with 8-bit
+ * GPIO expander," Doc ID 14489 Rev 6, CD00186725, STMicroelectronics"
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/input/stmpe811.h>
+
+#include "stmpe811.h"
+
+#if defined(CONFIG_INPUT) && defined(CONFIG_INPUT_STMPE811) && !defined(CONFIG_STMPE811_ADC_DISABLE)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stmpe811_adcinitialize
+ *
+ * Description:
+ * Configure for ADC mode operation. Set overall ADC ADC timing that
+ * applies to all pins.
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int stmpe811_adcinitialize(STMPE811_HANDLE handle)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle;
+ uint8_t regval;
+ int ret;
+
+ DEBUGASSERT(handle);
+
+ /* Get exclusive access to the device structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ int errval = errno;
+ idbg("sem_wait failed: %d\n", errval);
+ return -errval;
+ }
+
+ /* Enable Clocking for ADC */
+
+ regval = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2);
+ regval &= ~SYS_CTRL2_ADC_OFF;
+ stmpe811_putreg8(priv, STMPE811_SYS_CTRL2, regval);
+
+ /* Select Sample Time, bit number and ADC Reference */
+
+ stmpe811_putreg8(priv, STMPE811_ADC_CTRL1, priv->config->ctrl1);
+
+ /* Wait for 20 ms */
+
+ up_mdelay(20);
+
+ /* Select the ADC clock speed */
+
+ stmpe811_putreg8(priv, STMPE811_ADC_CTRL2, priv->config->ctrl2);
+
+ /* Mark ADC initialized */
+
+ priv->flags |= STMPE811_FLAGS_ADC_INITIALIZED;
+ sem_post(&priv->exclsem);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: stmpe811_adcconfig
+ *
+ * Description:
+ * Configure a pin for ADC input.
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ * pin - The ADC pin number
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int stmpe811_adcconfig(STMPE811_HANDLE handle, int pin)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle;
+ uint8_t pinmask = GPIO_PIN(pin);
+ uint8_t regval;
+ int ret;
+
+ DEBUGASSERT(handle && (unsigned)pin < STMPE811_ADC_NPINS);
+
+ /* Get exclusive access to the device structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ int errval = errno;
+ idbg("sem_wait failed: %d\n", errval);
+ return -errval;
+ }
+
+ /* Make sure that the pin is not already in use */
+
+ if ((priv->inuse & pinmask) != 0)
+ {
+ idbg("PIN%d is already in-use\n", pin);
+ sem_post(&priv->exclsem);
+ return -EBUSY;
+ }
+
+ /* Clear the alternate function bit for the pin, making it an ADC input
+ * (or perhaps an an external reference, depending on the state of the
+ * ADC_CTRL1_REF_SEL bit).
+ */
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_AF);
+ regval &= ~pinmask;
+ stmpe811_putreg8(priv, STMPE811_GPIO_AF, regval);
+
+ /* Mark the pin as 'in use' */
+
+ priv->inuse |= pinmask;
+ sem_post(&priv->exclsem);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: stmpe811_adcread
+ *
+ * Description:
+ * Read the converted analog input value from the select pin.
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ * pin - The ADC pin number
+ *
+ * Returned Value:
+ * The converted value (there is no error reporting mechanism).
+ *
+ ****************************************************************************/
+
+uint16_t stmpe811_adcread(STMPE811_HANDLE handle, int pin)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle;
+ uint8_t pinmask = GPIO_PIN(pin);
+ uint8_t regval;
+ int i;
+ int ret;
+
+ DEBUGASSERT(handle && (unsigned)pin < 8);
+
+ /* Get exclusive access to the device structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ int errval = errno;
+ idbg("sem_wait failed: %d\n", errval);
+ return -errval;
+ }
+
+ /* Request AD conversion by setting the bit corresponding the pin in the
+ * ADC CAPT register.
+ */
+
+ stmpe811_putreg8(priv, STMPE811_ADC_CAPT, pinmask);
+
+ /* Wait for the conversion to complete. The ADC CAPT register reads '1'
+ * if conversion is completed. Reads '0' if conversion is in progress.
+ * Try three times before giving up.
+ */
+
+ for (i = 0; i < 3; i++)
+ {
+ /* The worst case ADC conversion time is (nominally) 56.4 uS. The
+ * following usleep() looks nice but in reality, the usleep()
+ * does not have that kind of precision (it will probably end up
+ * waiting 10 MS).
+ */
+
+ usleep(57);
+
+ /* Check if the conversion is complete */
+
+ regval = stmpe811_getreg8(priv, STMPE811_ADC_CAPT);
+ if ((regval & pinmask) != 0)
+ {
+ break;
+ }
+ }
+
+ /* At the completion of the conversion, return whatever we read from
+ * from the channel register associated with the pin.
+ */
+
+ return stmpe811_getreg16(priv, STMPE811_ADC_DATACH(pin));
+}
+
+#endif /* CONFIG_INPUT && CONFIG_INPUT_STMPE811 && !CONFIG_STMPE811_ADC_DISABLE */
+
diff --git a/nuttx/drivers/input/stmpe811_base.c b/nuttx/drivers/input/stmpe811_base.c
new file mode 100644
index 000000000..f37c24622
--- /dev/null
+++ b/nuttx/drivers/input/stmpe811_base.c
@@ -0,0 +1,546 @@
+/****************************************************************************
+ * drivers/input/stmpe811_base.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "STMPE811 S-Touch® advanced resistive touchscreen controller with 8-bit
+ * GPIO expander," Doc ID 14489 Rev 6, CD00186725, STMicroelectronics"
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/input/stmpe811.h>
+
+#include "stmpe811.h"
+
+#if defined(CONFIG_INPUT) && defined(CONFIG_INPUT_STMPE811)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* If only a single STMPE811 device is supported, then the driver state
+ * structure may as well be pre-allocated.
+ */
+
+#ifndef CONFIG_STMPE811_MULTIPLE
+static struct stmpe811_dev_s g_stmpe811;
+
+/* Otherwise, we will need to maintain allocated driver instances in a list */
+
+#else
+static struct stmpe811_dev_s *g_stmpe811list;
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stmpe811_worker
+ *
+ * Description:
+ * This is the "bottom half" of the STMPE811 interrupt handler
+ *
+ ****************************************************************************/
+
+static void stmpe811_worker(FAR void *arg)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)arg;
+ uint8_t regval;
+
+ DEBUGASSERT(priv && priv->config);
+
+ /* Get the global interrupt status */
+
+ regval = stmpe811_getreg8(priv, STMPE811_INT_STA);
+
+ /* Check for a touchscreen interrupt */
+
+#ifndef CONFIG_STMPE811_TSC_DISABLE
+ if ((regval & (INT_TOUCH_DET|INT_FIFO_TH|INT_FIFO_OFLOW)) != 0)
+ {
+ /* Dispatch the touchscreen interrupt if it was brought into the link */
+
+#ifdef CONFIG_HAVE_WEAKFUNCTIONS
+ if (stmpe811_tscworker)
+#endif
+ {
+ stmpe811_tscworker(priv, regval);
+ }
+
+ stmpe811_putreg8(priv, STMPE811_INT_STA, (INT_TOUCH_DET|INT_FIFO_TH|INT_FIFO_OFLOW));
+ regval &= ~(INT_TOUCH_DET|INT_FIFO_TH|INT_FIFO_OFLOW);
+ }
+#endif
+
+#if !defined(CONFIG_STMPE811_GPIO_DISABLE) && !defined(CONFIG_STMPE811_GPIOINT_DISABLE)
+ if ((regval & INT_GPIO) != 0)
+ {
+ /* Dispatch the GPIO interrupt if it was brought into the link */
+
+#ifdef CONFIG_HAVE_WEAKFUNCTIONS
+ if (stmpe811_gpioworker)
+#endif
+ {
+ stmpe811_gpioworker(priv);
+ }
+
+ stmpe811_putreg8(priv, STMPE811_INT_STA, INT_GPIO);
+ regval &= ~INT_GPIO;
+ }
+#endif
+
+ /* Clear any other residual, unhandled pending interrupt */
+
+ if (regval != 0)
+ {
+ stmpe811_putreg8(priv, STMPE811_INT_STA, regval);
+ }
+
+ /* Re-enable the STMPE811 GPIO interrupt */
+
+ priv->config->enable(priv->config, true);
+}
+
+/****************************************************************************
+ * Name: stmpe811_interrupt
+ *
+ * Description:
+ * The STMPE811 interrupt handler
+ *
+ ****************************************************************************/
+
+static int stmpe811_interrupt(int irq, FAR void *context)
+{
+ FAR struct stmpe811_dev_s *priv;
+ FAR struct stmpe811_config_s *config;
+ int ret;
+
+ /* Which STMPE811 device caused the interrupt? */
+
+#ifndef CONFIG_STMPE811_MULTIPLE
+ priv = &g_stmpe811;
+#else
+ for (priv = g_stmpe811list;
+ priv && priv->config->irq != irq;
+ priv = priv->flink);
+
+ ASSERT(priv != NULL);
+#endif
+
+ /* Get a pointer the callbacks for convenience (and so the code is not so
+ * ugly).
+ */
+
+ config = priv->config;
+ DEBUGASSERT(config != NULL);
+
+ /* Disable further interrupts */
+
+ config->enable(config, false);
+
+ /* Check if interrupt work is already queue. If it is already busy, then
+ * we already have interrupt processing in the pipeline and we need to do
+ * nothing more.
+ */
+
+ if (work_available(&priv->work))
+ {
+ /* Yes.. Transfer processing to the worker thread. Since STMPE811
+ * interrupts are disabled while the work is pending, no special
+ * action should be required to protect the work queue.
+ */
+
+ ret = work_queue(HPWORK, &priv->work, stmpe811_worker, priv, 0);
+ if (ret != 0)
+ {
+ illdbg("Failed to queue work: %d\n", ret);
+ }
+ }
+
+ /* Clear any pending interrupts and return success */
+
+ config->clear(config);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: stmpe811_checkid
+ *
+ * Description:
+ * Read and verify the STMPE811 chip ID
+ *
+ ****************************************************************************/
+
+static int stmpe811_checkid(FAR struct stmpe811_dev_s *priv)
+{
+ uint16_t devid = 0;
+
+ /* Read device ID */
+
+ devid = stmpe811_getreg8(priv, STMPE811_CHIP_ID);
+ devid = (uint32_t)(devid << 8);
+ devid |= (uint32_t)stmpe811_getreg8(priv, STMPE811_CHIP_ID+1);
+ ivdbg("devid: %04x\n", devid);
+
+ if (devid != (uint16_t)CHIP_ID)
+ {
+ /* ID is not Correct */
+
+ return -ENODEV;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: stmpe811_reset
+ *
+ * Description:
+ * Reset the STMPE811
+ *
+ ****************************************************************************/
+
+static void stmpe811_reset(FAR struct stmpe811_dev_s *priv)
+{
+ /* Power Down the STMPE811 */
+
+ stmpe811_putreg8(priv, STMPE811_SYS_CTRL1, SYS_CTRL1_SOFTRESET);
+
+ /* Wait a bit */
+
+ usleep(20*1000);
+
+ /* Then power on again. All registers will be in their reset state. */
+
+ stmpe811_putreg8(priv, STMPE811_SYS_CTRL1, 0);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stmpe811_instantiate
+ *
+ * Description:
+ * Instantiate and configure the STMPE811 device driver to use the provided
+ * I2C or SPIdevice instance.
+ *
+ * Input Parameters:
+ * dev - An I2C or SPI driver instance
+ * config - Persistant board configuration data
+ *
+ * Returned Value:
+ * A non-zero handle is returned on success. This handle may then be used
+ * to configure the STMPE811 driver as necessary. A NULL handle value is
+ * returned on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STMPE811_SPI
+STMPE811_HANDLE stmpe811_instantiate(FAR struct spi_dev_s *dev,
+ FAR struct stmpe811_config_s *config)
+#else
+STMPE811_HANDLE stmpe811_instantiate(FAR struct i2c_dev_s *dev,
+ FAR struct stmpe811_config_s *config)
+#endif
+{
+ FAR struct stmpe811_dev_s *priv;
+ uint8_t regval;
+ int ret;
+
+ /* Allocate the device state structure */
+
+#ifdef CONFIG_STMPE811_MULTIPLE
+ priv = (FAR struct stmpe811_dev_s *)kzalloc(sizeof(struct stmpe811_dev_s));
+ if (!priv)
+ {
+ return NULL;
+ }
+
+ /* And save the device structure in the list of STMPE811 so that we can find it later */
+
+ priv->flink = g_stmpe811list;
+ g_stmpe811list = priv;
+#else
+
+ /* Use the one-and-only STMPE811 driver instance */
+
+ priv = &g_stmpe811;
+#endif
+
+ /* Initialize the device state structure */
+
+ sem_init(&priv->exclsem, 0, 1);
+ priv->config = config;
+
+#ifdef CONFIG_STMPE811_SPI
+ priv->spi = dev;
+#else
+ priv->i2c = dev;
+
+ /* Set the I2C address and frequency. REVISIT: This logic would be
+ * insufficient if we share the I2C bus with any other devices that also
+ * modify the address and frequency.
+ */
+
+ I2C_SETADDRESS(dev, config->address, 7);
+ I2C_SETFREQUENCY(dev, config->frequency);
+#endif
+
+ /* Read and verify the STMPE811 chip ID */
+
+ ret = stmpe811_checkid(priv);
+ if (ret < 0)
+ {
+#ifdef CONFIG_STMPE811_MULTIPLE
+ kfree(priv);
+#endif
+ return NULL;
+ }
+
+ /* Generate STMPE811 Software reset */
+
+ stmpe811_reset(priv);
+
+ /* Configure the interrupt output pin to generate interrupts on high or low level. */
+
+ regval = stmpe811_getreg8(priv, STMPE811_INT_CTRL);
+#ifdef CONFIG_STMPE811_ACTIVELOW
+ regval &= ~INT_CTRL_INT_POLARITY; /* Pin polarity: Active low / falling edge */
+#else
+ regval |= INT_CTRL_INT_POLARITY; /* Pin polarity: Active high / rising edge */
+#endif
+#ifdef CONFIG_STMPE811_EDGE
+ regval |= INT_CTRL_INT_TYPE; /* Edge interrupt */
+#else
+ regval &= ~INT_CTRL_INT_TYPE; /* Level interrupt */
+#endif
+ stmpe811_putreg8(priv, STMPE811_INT_CTRL, regval);
+
+ /* Attach the STMPE811 interrupt handler. */
+
+ config->attach(config, stmpe811_interrupt);
+
+ /* Clear any pending interrupts */
+
+ stmpe811_putreg8(priv, STMPE811_INT_STA, INT_ALL);
+ config->clear(config);
+ config->enable(config, true);
+
+ /* Enable global interrupts */
+
+ regval = stmpe811_getreg8(priv, STMPE811_INT_CTRL);
+ regval |= INT_CTRL_GLOBAL_INT;
+ stmpe811_putreg8(priv, STMPE811_INT_CTRL, regval);
+
+ /* Return our private data structure as an opaque handle */
+
+ return (STMPE811_HANDLE)priv;
+}
+
+/****************************************************************************
+ * Name: stmpe811_getreg8
+ *
+ * Description:
+ * Read from an 8-bit STMPE811 register
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STMPE811_I2C
+uint8_t stmpe811_getreg8(FAR struct stmpe811_dev_s *priv, uint8_t regaddr)
+{
+ /* 8-bit data read sequence:
+ *
+ * Start - I2C_Write_Address - STMPE811_Reg_Address -
+ * Repeated_Start - I2C_Read_Address - STMPE811_Read_Data - STOP
+ */
+
+ struct i2c_msg_s msg[2];
+ uint8_t regval;
+ int ret;
+
+ /* Setup 8-bit STMPE811 address write message */
+
+ msg[0].addr = priv->config->address; /* 7-bit address */
+ msg[0].flags = 0; /* Write transaction, beginning with START */
+ msg[0].buffer = &regaddr; /* Transfer from this address */
+ msg[0].length = 1; /* Send one byte following the address
+ * (no STOP) */
+
+ /* Set up the 8-bit STMPE811 data read message */
+
+ msg[1].addr = priv->config->address; /* 7-bit address */
+ msg[1].flags = I2C_M_READ; /* Read transaction, beginning with Re-START */
+ msg[1].buffer = &regval; /* Transfer to this address */
+ msg[1].length = 1; /* Receive one byte following the address
+ * (then STOP) */
+
+ /* Perform the transfer */
+
+ ret = I2C_TRANSFER(priv->i2c, msg, 2);
+ if (ret < 0)
+ {
+ idbg("I2C_TRANSFER failed: %d\n", ret);
+ return 0;
+ }
+
+#ifdef CONFIG_STMPE811_REGDEBUG
+ dbg("%02x->%02x\n", regaddr, regval);
+#endif
+ return regval;
+}
+#endif
+
+/****************************************************************************
+ * Name: stmpe811_putreg8
+ *
+ * Description:
+ * Write a value to an 8-bit STMPE811 register
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STMPE811_I2C
+void stmpe811_putreg8(FAR struct stmpe811_dev_s *priv,
+ uint8_t regaddr, uint8_t regval)
+{
+ /* 8-bit data read sequence:
+ *
+ * Start - I2C_Write_Address - STMPE811_Reg_Address - STMPE811_Write_Data - STOP
+ */
+
+ struct i2c_msg_s msg;
+ uint8_t txbuffer[2];
+ int ret;
+
+#ifdef CONFIG_STMPE811_REGDEBUG
+ dbg("%02x<-%02x\n", regaddr, regval);
+#endif
+
+ /* Setup to the data to be transferred. Two bytes: The STMPE811 register
+ * address followed by one byte of data.
+ */
+
+ txbuffer[0] = regaddr;
+ txbuffer[1] = regval;
+
+ /* Setup 8-bit STMPE811 address write message */
+
+ msg.addr = priv->config->address; /* 7-bit address */
+ msg.flags = 0; /* Write transaction, beginning with START */
+ msg.buffer = txbuffer; /* Transfer from this address */
+ msg.length = 2; /* Send two byte following the address
+ * (then STOP) */
+
+ /* Perform the transfer */
+
+ ret = I2C_TRANSFER(priv->i2c, &msg, 1);
+ if (ret < 0)
+ {
+ idbg("I2C_TRANSFER failed: %d\n", ret);
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: stmpe811_getreg16
+ *
+ * Description:
+ * Read 16-bits of data from an STMPE-11 register
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STMPE811_I2C
+uint16_t stmpe811_getreg16(FAR struct stmpe811_dev_s *priv, uint8_t regaddr)
+{
+ /* 16-bit data read sequence:
+ *
+ * Start - I2C_Write_Address - STMPE811_Reg_Address -
+ * Repeated_Start - I2C_Read_Address - STMPE811_Read_Data_1 -
+ * STMPE811_Read_Data_2 - STOP
+ */
+
+
+ struct i2c_msg_s msg[2];
+ uint8_t rxbuffer[2];
+ int ret;
+
+ /* Setup 8-bit STMPE811 address write message */
+
+ msg[0].addr = priv->config->address; /* 7-bit address */
+ msg[0].flags = 0; /* Write transaction, beginning with START */
+ msg[0].buffer = &regaddr; /* Transfer from this address */
+ msg[0].length = 1; /* Send one byte following the address
+ * (no STOP) */
+
+ /* Set up the 8-bit STMPE811 data read message */
+
+ msg[1].addr = priv->config->address; /* 7-bit address */
+ msg[1].flags = I2C_M_READ; /* Read transaction, beginning with Re-START */
+ msg[1].buffer = rxbuffer; /* Transfer to this address */
+ msg[1].length = 2; /* Receive two bytes following the address
+ * (then STOP) */
+
+ /* Perform the transfer */
+
+ ret = I2C_TRANSFER(priv->i2c, msg, 2);
+ if (ret < 0)
+ {
+ idbg("I2C_TRANSFER failed: %d\n", ret);
+ return 0;
+ }
+
+#ifdef CONFIG_STMPE811_REGDEBUG
+ dbg("%02x->%02x%02x\n", regaddr, rxbuffer[0], rxbuffer[1]);
+#endif
+ return (uint16_t)rxbuffer[0] << 8 | (uint16_t)rxbuffer[1];
+}
+#endif
+
+#endif /* CONFIG_INPUT && CONFIG_INPUT_STMPE811 */
+
diff --git a/nuttx/drivers/input/stmpe811_gpio.c b/nuttx/drivers/input/stmpe811_gpio.c
new file mode 100644
index 000000000..b545828d1
--- /dev/null
+++ b/nuttx/drivers/input/stmpe811_gpio.c
@@ -0,0 +1,454 @@
+/****************************************************************************
+ * drivers/input/stmpe811_gpio.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "STMPE811 S-Touch® advanced resistive touchscreen controller with 8-bit
+ * GPIO expander," Doc ID 14489 Rev 6, CD00186725, STMicroelectronics"
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/input/stmpe811.h>
+
+#include "stmpe811.h"
+
+#if defined(CONFIG_INPUT) && defined(CONFIG_INPUT_STMPE811) && !defined(CONFIG_STMPE811_GPIO_DISABLE)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stmpe811_gpioinit
+ *
+ * Description:
+ * Initialize the GPIO interrupt subsystem
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static void stmpe811_gpioinit(FAR struct stmpe811_dev_s *priv)
+{
+ uint8_t regval;
+
+ if ((priv->flags & STMPE811_FLAGS_GPIO_INITIALIZED) == 0)
+ {
+ /* Enable Clocking for GPIO */
+
+ regval = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2);
+ regval &= ~SYS_CTRL2_GPIO_OFF;
+ stmpe811_putreg8(priv, STMPE811_SYS_CTRL2, regval);
+
+ /* Disable all GPIO interrupts */
+
+ stmpe811_putreg8(priv, STMPE811_GPIO_EN, 0);
+
+ /* Enable global GPIO interrupts */
+
+#ifndef CONFIG_STMPE811_GPIOINT_DISABLE
+ regval = stmpe811_getreg8(priv, STMPE811_INT_EN);
+ regval |= INT_GPIO;
+ stmpe811_putreg8(priv, STMPE811_INT_EN, regval);
+#endif
+
+ priv->flags |= STMPE811_FLAGS_GPIO_INITIALIZED;
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stmpe811_gpioconfig
+ *
+ * Description:
+ * Configure an STMPE811 GPIO pin
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ * pinconfig - Bit-encoded pin configuration
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int stmpe811_gpioconfig(STMPE811_HANDLE handle, uint8_t pinconfig)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle;
+ int pin = (pinconfig & STMPE811_GPIO_PIN_MASK) >> STMPE811_GPIO_PIN_SHIFT;
+ uint8_t pinmask = (1 << pin);
+ uint8_t regval;
+ int ret;
+
+ DEBUGASSERT(handle && (unsigned)pin < STMPE811_GPIO_NPINS);
+
+ /* Get exclusive access to the device structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ int errval = errno;
+ idbg("sem_wait failed: %d\n", errval);
+ return -errval;
+ }
+
+ /* Make sure that the pin is not already in use */
+
+ if ((priv->inuse & pinmask) != 0)
+ {
+ idbg("PIN%d is already in-use\n", pin);
+ sem_post(&priv->exclsem);
+ return -EBUSY;
+ }
+
+ /* Make sure that the GPIO block has been initialized */
+
+ stmpe811_gpioinit(priv);
+
+ /* Set the alternate function bit for the pin, making it a GPIO */
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_AF);
+ regval |= pinmask;
+ stmpe811_putreg8(priv, STMPE811_GPIO_AF, regval);
+
+ /* Is the pin an input or an output? */
+
+ if ((pinconfig & STMPE811_GPIO_DIR) == STMPE811_GPIO_OUTPUT)
+ {
+ /* The pin is an output */
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_DIR);
+ regval &= ~pinmask;
+ stmpe811_putreg8(priv, STMPE811_GPIO_DIR, regval);
+
+ /* Set its initial output value */
+
+ stmpe811_gpiowrite(handle, pinconfig,
+ (pinconfig & STMPE811_GPIO_VALUE) != STMPE811_GPIO_ZERO);
+ }
+ else
+ {
+ /* It is an input */
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_DIR);
+ regval |= pinmask;
+ stmpe811_putreg8(priv, STMPE811_GPIO_DIR, regval);
+
+ /* Set up the falling edge detection */
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_FE);
+ if ((pinconfig & STMPE811_GPIO_FALLING) != 0)
+ {
+ regval |= pinmask;
+ }
+ else
+ {
+ regval &= pinmask;
+ }
+ stmpe811_putreg8(priv, STMPE811_GPIO_FE, regval);
+
+ /* Set up the rising edge detection */
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_RE);
+ if ((pinconfig & STMPE811_GPIO_FALLING) != 0)
+ {
+ regval |= pinmask;
+ }
+ else
+ {
+ regval &= pinmask;
+ }
+ stmpe811_putreg8(priv, STMPE811_GPIO_RE, regval);
+
+ /* Disable interrupts for now */
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_EN);
+ regval &= ~pinmask;
+ stmpe811_putreg8(priv, STMPE811_GPIO_EN, regval);
+ }
+
+ /* Mark the pin as 'in use' */
+
+ priv->inuse |= pinmask;
+ sem_post(&priv->exclsem);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: stmpe811_gpiowrite
+ *
+ * Description:
+ * Set or clear the GPIO output
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ * pinconfig - Bit-encoded pin configuration
+ * value = true: write logic '1'; false: write logic '0;
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void stmpe811_gpiowrite(STMPE811_HANDLE handle, uint8_t pinconfig, bool value)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle;
+ int pin = (pinconfig & STMPE811_GPIO_PIN_MASK) >> STMPE811_GPIO_PIN_SHIFT;
+ int ret;
+
+ DEBUGASSERT(handle && (unsigned)pin < STMPE811_GPIO_NPINS);
+
+ /* Get exclusive access to the device structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ idbg("sem_wait failed: %d\n", errno);
+ return;
+ }
+
+ /* Are we setting or clearing outputs? */
+
+ if (value)
+ {
+ /* Set the output valu(s)e by writing to the SET register */
+
+ stmpe811_putreg8(priv, STMPE811_GPIO_SETPIN, (1 << pin));
+ }
+ else
+ {
+ /* Clear the output value(s) by writing to the CLR register */
+
+ stmpe811_putreg8(priv, STMPE811_GPIO_CLRPIN, (1 << pin));
+ }
+
+ sem_post(&priv->exclsem);
+}
+
+/****************************************************************************
+ * Name: stmpe811_gpioread
+ *
+ * Description:
+ * Set or clear the GPIO output
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ * pinconfig - Bit-encoded pin configuration
+ * value - The location to return the state of the GPIO pin
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int stmpe811_gpioread(STMPE811_HANDLE handle, uint8_t pinconfig, bool *value)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle;
+ int pin = (pinconfig & STMPE811_GPIO_PIN_MASK) >> STMPE811_GPIO_PIN_SHIFT;
+ uint8_t regval;
+ int ret;
+
+ DEBUGASSERT(handle && (unsigned)pin < STMPE811_GPIO_NPINS);
+
+ /* Get exclusive access to the device structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ int errval = errno;
+ idbg("sem_wait failed: %d\n", errval);
+ return -errval;
+ }
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_MPSTA);
+ *value = ((regval & GPIO_PIN(pin)) != 0);
+ sem_post(&priv->exclsem);
+ return OK;
+}
+
+/***********************************************************************************
+ * Name: stmpe811_gpioattach
+ *
+ * Description:
+ * Attach to a GPIO interrupt input pin and enable interrupts on the pin. Using
+ * the value NULL for the handler address will disable interrupts from the pin and
+ * detach the handler.
+ *
+ * NOTE: Callbacks do not occur from an interrupt handler but rather from the
+ * context of the worker thread.
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ * pinconfig - Bit-encoded pin configuration
+ * handler - The handler that will be called when the interrupt occurs.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is returned
+ * to indicate the nature of the failure.
+ *
+ ************************************************************************************/
+
+#ifndef CONFIG_STMPE811_GPIOINT_DISABLE
+int stmpe811_gpioattach(STMPE811_HANDLE handle, uint8_t pinconfig,
+ stmpe811_handler_t handler)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle;
+ int pin = (pinconfig & STMPE811_GPIO_PIN_MASK) >> STMPE811_GPIO_PIN_SHIFT;
+ uint8_t regval;
+ int ret;
+
+ DEBUGASSERT(handle && (unsigned)pin < STMPE811_GPIO_NPINS);
+
+ /* Get exclusive access to the device structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ int errval = errno;
+ idbg("sem_wait failed: %d\n", errval);
+ return -errval;
+ }
+
+ /* Make sure that the GPIO interrupt system has been gpioinitialized */
+
+ stmpe811_gpioinit(priv);
+
+ /* Set/clear the handler */
+
+ priv->handlers[pin] = handler;
+
+ /* If an handler has provided, then we are enabling interrupts */
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_EN);
+ if (handler)
+ {
+ /* Enable interrupts for this GPIO */
+
+ regval &= ~GPIO_PIN(pin);
+ }
+ else
+ {
+ /* Disable interrupts for this GPIO */
+
+ regval &= ~GPIO_PIN(pin);
+ }
+ stmpe811_putreg8(priv, STMPE811_GPIO_EN, regval);
+
+ sem_post(&priv->exclsem);
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: stmpe811_gpioworker
+ *
+ * Description:
+ * Handle GPIO interrupt events (this function actually executes in the
+ * context of the worker thread).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_STMPE811_GPIOINT_DISABLE
+void stmpe811_gpioworker(FAR struct stmpe811_dev_s *priv)
+{
+ uint8_t regval;
+ uint8_t pinmask;
+ int pin;
+
+ /* Get the set of pending GPIO interrupts */
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_INTSTA);
+
+ /* Look at each pin */
+
+ for (pin = 0; pin < STMPE811_GPIO_NPINS; pin++)
+ {
+ pinmask = GPIO_INT(pin);
+ if ((regval & pinmask) != 0)
+ {
+ /* Check if we have a handler for this interrupt (there should
+ * be one)
+ */
+
+ if (priv->handlers[pin])
+ {
+ /* Interrupt is pending... dispatch the interrupt to the
+ * callback
+ */
+
+ priv->handlers[pin](pin);
+ }
+ else
+ {
+ illdbg("No handler for PIN%d, GPIO_INTSTA: %02x\n", pin, regval);
+ }
+
+ /* Clear the pending GPIO interrupt by writing a '1' to the
+ * pin position in the status register.
+ */
+
+ stmpe811_putreg8(priv, STMPE811_GPIO_INTSTA, pinmask);
+ }
+ }
+}
+#endif
+
+#endif /* CONFIG_INPUT && CONFIG_INPUT_STMPE811 && !CONFIG_STMPE811_GPIO_DISABLE */
+
diff --git a/nuttx/drivers/input/stmpe811_temp.c b/nuttx/drivers/input/stmpe811_temp.c
new file mode 100644
index 000000000..0fefd2069
--- /dev/null
+++ b/nuttx/drivers/input/stmpe811_temp.c
@@ -0,0 +1,174 @@
+/****************************************************************************
+ * drivers/input/stmpe811_temp.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "STMPE811 S-Touch® advanced resistive touchscreen controller with 8-bit
+ * GPIO expander," Doc ID 14489 Rev 6, CD00186725, STMicroelectronics"
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/input/stmpe811.h>
+
+#include "stmpe811.h"
+
+#if defined(CONFIG_INPUT) && defined(CONFIG_INPUT_STMPE811) && !defined(CONFIG_STMPE811_TEMP_DISABLE)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stmpe811_tempinitialize
+ *
+ * Description:
+ * Configure the temperature sensor.
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int stmpe811_tempinitialize(STMPE811_HANDLE handle)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle;
+ uint8_t regval;
+
+ /* Enable clocking for ADC and the temperature sensor */
+
+ regval = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2);
+ regval &= ~(SYS_CTRL2_TS_OFF | SYS_CTRL2_ADC_OFF);
+ stmpe811_putreg8(priv, STMPE811_SYS_CTRL2, regval);
+
+ /* Enable the temperature sensor */
+
+ stmpe811_putreg8(priv, STMPE811_TEMP_CTRL, TEMP_CTRL_ENABLE);
+
+ /* Aquire data enable */
+
+ stmpe811_putreg8(priv, STMPE811_TEMP_CTRL, (TEMP_CTRL_ACQ|TEMP_CTRL_ENABLE));
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: stmpe811_tempread
+ *
+ * Description:
+ * Configure the temperature sensor.
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+uint16_t stmpe811_tempread(STMPE811_HANDLE handle)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle;
+ uint32_t temp = 0;
+ uint8_t temp1;
+ uint8_t temp2;
+
+ /* Acquire data enable */
+
+ stmpe811_putreg8(priv, STMPE811_TEMP_CTRL, (TEMP_CTRL_ACQ|TEMP_CTRL_ENABLE));
+
+ /* Read the temperature */
+
+ temp1 = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2);
+ temp2 = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2+1);
+
+ /* Scale the temperature (where Vio is assumed to be .33) */
+
+ temp = ((uint32_t)(temp1 & 3) << 8) | temp2;
+ temp = (uint32_t)((33 * temp * 100) / 751);
+ temp = (uint32_t)((temp + 5) / 10);
+
+ return (uint16_t)temp;
+}
+
+/****************************************************************************
+ * Name: stmpe811_tempinterrupt
+ *
+ * Description:
+ * Configure the temperature sensor to sample the temperature periodically.
+ * Set the temperature threshold to generate an interrupt and notify
+ * to the client using the provide callback function pointer.
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_instantiate
+ * threshold - The threshold temperature value
+ * direction - True: Generate an interrupt if the temperate exceeds the
+ * threshold value; False: Generate an interrupt if the
+ * temperature falls below the threshold value.
+ * callback - The client callback function that will be called when
+ * the termperature crosses the threshold.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+/* Not implemented */
+
+#endif /* CONFIG_INPUT && CONFIG_INPUT_STMPE811 && !CONFIG_STMPE811_TEMP_DISABLE */
+
diff --git a/nuttx/drivers/input/stmpe811_tsc.c b/nuttx/drivers/input/stmpe811_tsc.c
new file mode 100644
index 000000000..c7f8b473b
--- /dev/null
+++ b/nuttx/drivers/input/stmpe811_tsc.c
@@ -0,0 +1,1144 @@
+/****************************************************************************
+ * drivers/input/stmpe811_tsc.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "STMPE811 S-Touch® advanced resistive touchscreen controller with 8-bit
+ * GPIO expander," Doc ID 14489 Rev 6, CD00186725, STMicroelectronics"
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <semaphore.h>
+
+#include <poll.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/i2c.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/input/touchscreen.h>
+#include <nuttx/input/stmpe811.h>
+
+#include "stmpe811.h"
+
+#if defined(CONFIG_INPUT) && defined(CONFIG_INPUT_STMPE811) && !defined(CONFIG_STMPE811_TSC_DISABLE)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define Direction_IN 0x00
+#define Direction_OUT 0x01
+
+#define Polarity_Low 0x00
+#define Polarity_High 0x04
+#define Type_Level 0x00
+#define Type_Edge 0x02
+
+#define IO_IT_0 0x01
+#define IO_IT_1 0x02
+#define IO_IT_2 0x04
+#define IO_IT_3 0x08
+#define IO_IT_4 0x10
+#define IO_IT_5 0x20
+#define IO_IT_6 0x40
+#define IO_IT_7 0x80
+#define ALL_IT 0xFF
+#define IOE_JOY_IT (uint8_t)(IO_IT_3 | IO_IT_4 | IO_IT_5 | IO_IT_6 | IO_IT_7)
+#define IOE_TS_IT (uint8_t)(IO_IT_0 | IO_IT_1 | IO_IT_2)
+#define IOE_INMEMS_IT (uint8_t)(IO_IT_2 | IO_IT_3)
+
+#define EDGE_FALLING 0x01
+#define EDGE_RISING 0x02
+
+#define TIMEOUT_MAX 0x3000 /*<! The value of the maximal timeout for I2C waiting loops */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* Internal logic */
+
+static void stmpe811_notify(FAR struct stmpe811_dev_s *priv);
+static int stmpe811_sample(FAR struct stmpe811_dev_s *priv,
+ FAR struct stmpe811_sample_s *sample);
+static inline int stmpe811_waitsample(FAR struct stmpe811_dev_s *priv,
+ FAR struct stmpe811_sample_s *sample);
+
+/* Character driver methods */
+
+static int stmpe811_open(FAR struct file *filep);
+static int stmpe811_close(FAR struct file *filep);
+static ssize_t stmpe811_read(FAR struct file *filep, FAR char *buffer,
+ size_t len);
+static int stmpe811_ioctl(FAR struct file *filep, int cmd,
+ unsigned long arg);
+#ifndef CONFIG_DISABLE_POLL
+static int stmpe811_poll(FAR struct file *filep, struct pollfd *fds,
+ bool setup);
+#endif
+
+/* Initialization logic */
+
+static inline void stmpe811_tscinitialize(FAR struct stmpe811_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This the the vtable that supports the character driver interface */
+
+static const struct file_operations g_stmpe811fops =
+{
+ stmpe811_open, /* open */
+ stmpe811_close, /* close */
+ stmpe811_read, /* read */
+ 0, /* write */
+ 0, /* seek */
+ stmpe811_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , stmpe811_poll /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Name: stmpe811_notify
+ *
+ * Description:
+ * Notify any threads waiting on touchscreen data that data is now
+ * available for reading.
+ *
+ ****************************************************************************/
+
+static void stmpe811_notify(FAR struct stmpe811_dev_s *priv)
+{
+#ifndef CONFIG_DISABLE_POLL
+ int i;
+#endif
+
+ /* If there are threads waiting for read data, then signal one of them
+ * that the read data is available.
+ */
+
+ if (priv->nwaiters > 0)
+ {
+ /* After posting this semaphore, we need to exit because the STMPE811
+ * is no longer available.
+ */
+
+ sem_post(&priv->waitsem);
+ }
+
+ /* If there are threads waiting on poll() for STMPE811 data to become available,
+ * then wake them up now. NOTE: we wake up all waiting threads because we
+ * do not know that they are going to do. If they all try to read the data,
+ * then some make end up blocking after all.
+ */
+
+#ifndef CONFIG_DISABLE_POLL
+ for (i = 0; i < CONFIG_STMPE811_NPOLLWAITERS; i++)
+ {
+ struct pollfd *fds = priv->fds[i];
+ if (fds)
+ {
+ fds->revents |= POLLIN;
+ ivdbg("Report events: %02x\n", fds->revents);
+ sem_post(fds->sem);
+ }
+ }
+#endif
+}
+
+/****************************************************************************
+ * Name: stmpe811_sample
+ *
+ * Description:
+ * Check if touchscreen sample data is available now and, if so, return
+ * the sample data. This is part of the stmpe811_read logic.
+ *
+ * Assumption:
+ * Pre-emption is disable to prevent the worker thread from running.
+ * Otherwise, sampled data may continue to change.
+ *
+ ****************************************************************************/
+
+static int stmpe811_sample(FAR struct stmpe811_dev_s *priv,
+ FAR struct stmpe811_sample_s *sample)
+{
+ int ret = -EAGAIN;
+
+ /* Is there new STMPE811 sample data available? */
+
+ if (priv->penchange)
+ {
+ /* Yes.. the state has changed in some way. Return a copy of the
+ * sampled data.
+ */
+
+ memcpy(sample, &priv->sample, sizeof(struct stmpe811_sample_s));
+
+ /* Now manage state transitions */
+
+ if (sample->contact == CONTACT_UP)
+ {
+ /* The sampling logic has detected pen-up in some condition other
+ * than CONTACT_NONE. Set the next state to CONTACT_NONE: Further
+ * pen-down reports will be ignored. Increment the ID so that
+ * next contact ID will be unique
+ */
+
+ priv->sample.contact = CONTACT_NONE;
+ priv->sample.valid = false;
+ priv->id++;
+ }
+ else if (sample->contact == CONTACT_DOWN)
+ {
+ /* The sampling logic has detected pen-up in some condition other
+ * than CONTACT_MOVE. Set the next state to CONTACT_MOVE: Further
+ * samples collected while the pen is down will reported as movement
+ * events.
+ */
+
+ priv->sample.contact = CONTACT_MOVE;
+ }
+
+ priv->penchange = false;
+ ret = OK;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: stmpe811_waitsample
+ *
+ * Description:
+ * Wait for a sample to become available (this is really part of the
+ * stmpe811_read logic).
+ *
+ ****************************************************************************/
+
+static inline int stmpe811_waitsample(FAR struct stmpe811_dev_s *priv,
+ FAR struct stmpe811_sample_s *sample)
+{
+ int ret;
+
+ /* Disable pre-emption to prevent the worker thread from running
+ * asynchronously.
+ */
+
+ sched_lock();
+
+ /* Now release the semaphore that manages mutually exclusive access to
+ * the device structure. This may cause other tasks to become ready to
+ * run, but they cannot run yet because pre-emption is disabled.
+ */
+
+ sem_post(&priv->exclsem);
+
+ /* Try to get the a sample... if we cannot, then wait on the semaphore
+ * that is posted when new sample data is availble.
+ */
+
+ while (stmpe811_sample(priv, sample) < 0)
+ {
+ /* Wait for a change in the STMPE811 state */
+
+ priv->nwaiters++;
+ ret = sem_wait(&priv->waitsem);
+ priv->nwaiters--;
+
+ /* When we are re-awakened, pre-emption will again be disabled */
+
+ if (ret < 0)
+ {
+#ifdef CONFIG_DEBUG
+ // Sample the errno (debug output could change it)
+
+ int errval = errno;
+
+ /* If we are awakened by a signal, then we need to return
+ * the failure now.
+ */
+
+ idbg("ERROR: sem_wait failed: %d\n", errval);
+ DEBUGASSERT(errval == EINTR);
+#endif
+ ret = -EINTR;
+ goto errout;
+ }
+ }
+
+ /* Re-acquire the the semaphore that manages mutually exclusive access to
+ * the device structure. We may have to wait here. But we have our sample.
+ * Interrupts and pre-emption will be re-enabled while we wait.
+ */
+
+ ret = sem_wait(&priv->exclsem);
+
+errout:
+ /* Restore pre-emption. We might get suspended here but that is okay
+ * because we already have our sample. Note: this means that if there
+ * were two threads reading from the STMPE811 for some reason, the data
+ * might be read out of order.
+ */
+
+ sched_unlock();
+ return ret;
+}
+
+/****************************************************************************
+ * Name: stmpe811_open
+ *
+ * Description:
+ * Standard character driver open method.
+ *
+ ****************************************************************************/
+
+static int stmpe811_open(FAR struct file *filep)
+{
+#ifdef CONFIG_STMPE811_REFCNT
+ FAR struct inode *inode;
+ FAR struct stmpe811_dev_s *priv;
+ uint8_t tmp;
+ int ret;
+
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct stmpe811_dev_s *)inode->i_private;
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Increment the reference count */
+
+ tmp = priv->crefs + 1;
+ if (tmp == 0)
+ {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ goto errout_with_sem;
+ }
+
+ /* When the reference increments to 1, this is the first open event
+ * on the driver.. and an opportunity to do any one-time initialization.
+ */
+
+ /* Save the new open count on success */
+
+ priv->crefs = tmp;
+
+errout_with_sem:
+ sem_post(&priv->exclsem);
+ return ret;
+#else
+ return OK;
+#endif
+}
+
+/****************************************************************************
+ * Name: stmpe811_close
+ *
+ * Description:
+ * Standard character driver close method.
+ *
+ ****************************************************************************/
+
+static int stmpe811_close(FAR struct file *filep)
+{
+#ifdef CONFIG_STMPE811_REFCNT
+ FAR struct inode *inode;
+ FAR struct stmpe811_dev_s *priv;
+ int ret;
+
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct stmpe811_dev_s *)inode->i_private;
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Decrement the reference count unless it would decrement a negative
+ * value. When the count decrements to zero, there are no further
+ * open references to the driver.
+ */
+
+ if (priv->crefs >= 1)
+ {
+ priv->crefs--;
+ }
+
+ sem_post(&priv->exclsem);
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Name: stmpe811_read
+ *
+ * Description:
+ * Standard character driver read method.
+ *
+ ****************************************************************************/
+
+static ssize_t stmpe811_read(FAR struct file *filep, FAR char *buffer, size_t len)
+{
+ FAR struct inode *inode;
+ FAR struct stmpe811_dev_s *priv;
+ FAR struct touch_sample_s *report;
+ struct stmpe811_sample_s sample;
+ int ret;
+
+ ivdbg("len=%d\n", len);
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct stmpe811_dev_s *)inode->i_private;
+
+ /* Verify that the caller has provided a buffer large enough to receive
+ * the touch data.
+ */
+
+ if (len < SIZEOF_TOUCH_SAMPLE_S(1))
+ {
+ /* We could provide logic to break up a touch report into segments and
+ * handle smaller reads... but why?
+ */
+
+ return -ENOSYS;
+ }
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Try to read sample data. */
+
+ ret = stmpe811_sample(priv, &sample);
+ if (ret < 0)
+ {
+ /* Sample data is not available now. We would ave to wait to get
+ * receive sample data. If the user has specified the O_NONBLOCK
+ * option, then just return an error.
+ */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ ret = -EAGAIN;
+ goto errout;
+ }
+
+ /* Wait for sample data */
+
+ ret = stmpe811_waitsample(priv, &sample);
+ if (ret < 0)
+ {
+ /* We might have been awakened by a signal */
+
+ goto errout;
+ }
+ }
+
+ /* In any event, we now have sampled STMPE811 data that we can report
+ * to the caller.
+ */
+
+ report = (FAR struct touch_sample_s *)buffer;
+ memset(report, 0, SIZEOF_TOUCH_SAMPLE_S(1));
+ report->npoints = 1;
+ report->point[0].id = sample.id;
+ report->point[0].x = sample.x;
+ report->point[0].y = sample.y;
+ report->point[0].pressure = sample.z;
+
+ /* Report the appropriate flags */
+
+ if (sample.contact == CONTACT_UP)
+ {
+ /* Pen is now up. Is the positional data valid? This is important to
+ * know because the release will be sent to the window based on its
+ * last positional data.
+ */
+
+ if (sample.valid)
+ {
+ report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID |
+ TOUCH_POS_VALID | TOUCH_PRESSURE_VALID;
+ }
+ else
+ {
+ report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID;
+ }
+ }
+ else if (sample.contact == CONTACT_DOWN)
+ {
+ /* First contact */
+
+ report->point[0].flags = TOUCH_DOWN | TOUCH_ID_VALID |
+ TOUCH_POS_VALID | TOUCH_PRESSURE_VALID;
+ }
+ else /* if (sample->contact == CONTACT_MOVE) */
+ {
+ /* Movement of the same contact */
+
+ report->point[0].flags = TOUCH_MOVE | TOUCH_ID_VALID |
+ TOUCH_POS_VALID | TOUCH_PRESSURE_VALID;
+ }
+
+ ret = SIZEOF_TOUCH_SAMPLE_S(1);
+
+errout:
+ sem_post(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: stmpe811_ioctl
+ *
+ * Description:
+ * Standard character driver ioctl method.
+ *
+****************************************************************************/
+
+static int stmpe811_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode;
+ FAR struct stmpe811_dev_s *priv;
+ int ret;
+
+ ivdbg("cmd: %d arg: %ld\n", cmd, arg);
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct stmpe811_dev_s *)inode->i_private;
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Process the IOCTL by command */
+
+ switch (cmd)
+ {
+ case TSIOC_SETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
+ {
+ FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
+ DEBUGASSERT(priv->config != NULL && ptr != NULL);
+ priv->config->frequency = I2C_SETFREQUENCY(priv->i2c, *ptr);
+ }
+ break;
+
+ case TSIOC_GETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
+ {
+ FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
+ DEBUGASSERT(priv->config != NULL && ptr != NULL);
+ *ptr = priv->config->frequency;
+ }
+ break;
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ sem_post(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: stmpe811_poll
+ *
+ * Description:
+ * Standard character driver poll method.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static int stmpe811_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup)
+{
+ FAR struct inode *inode;
+ FAR struct stmpe811_dev_s *priv;
+ int ret;
+ int i;
+
+ ivdbg("setup: %d\n", (int)setup);
+ DEBUGASSERT(filep && fds);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct stmpe811_dev_s *)inode->i_private;
+
+ /* Are we setting up the poll? Or tearing it down? */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ if (setup)
+ {
+ /* Ignore waits that do not include POLLIN */
+
+ if ((fds->events & POLLIN) == 0)
+ {
+ idbg("ERROR: Missing POLLIN: revents: %08x\n", fds->revents);
+ ret = -EDEADLK;
+ goto errout;
+ }
+
+ /* This is a request to set up the poll. Find an available
+ * slot for the poll structure reference
+ */
+
+ for (i = 0; i < CONFIG_STMPE811_NPOLLWAITERS; i++)
+ {
+ /* Find an available slot */
+
+ if (!priv->fds[i])
+ {
+ /* Bind the poll structure and this slot */
+
+ priv->fds[i] = fds;
+ fds->priv = &priv->fds[i];
+ break;
+ }
+ }
+
+ if (i >= CONFIG_STMPE811_NPOLLWAITERS)
+ {
+ idbg("ERROR: No availabled slot found: %d\n", i);
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto errout;
+ }
+
+ /* Should we immediately notify on any of the requested events? */
+
+ if (priv->penchange)
+ {
+ stmpe811_notify(priv);
+ }
+ }
+ else if (fds->priv)
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+ DEBUGASSERT(slot != NULL);
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+errout:
+ sem_post(&priv->exclsem);
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: stmpe811_timeoutworker
+ *
+ * Description:
+ * A timer has expired without receiving a pen up event. Check again.
+ *
+ ****************************************************************************/
+
+static void stmpe811_timeoutworker(FAR void *arg)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)arg;
+
+ DEBUGASSERT(priv);
+
+ /* Treat the timeout just like an interrupt occurred */
+
+ stmpe811_tscworker(priv, stmpe811_getreg8(priv, STMPE811_INT_STA));
+}
+
+/****************************************************************************
+ * Name: stmpe811_timeout
+ *
+ * Description:
+ * A timer has expired without receiving a pen up event. Schedule work
+ * to check again.
+ *
+ ****************************************************************************/
+
+static void stmpe811_timeout(int argc, uint32_t arg1, ...)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)((uintptr_t)arg1);
+ int ret;
+
+ /* Are we still stuck in the pen down state? */
+
+ if (priv->sample.contact == CONTACT_MOVE ||
+ priv->sample.contact == CONTACT_MOVE)
+ {
+ /* Yes... is the worker thread available? If not, then apparently
+ * we have work already pending?
+ */
+
+ if (work_available(&priv->timeout))
+ {
+ /* Yes.. Transfer processing to the worker thread. Since STMPE811
+ * interrupts are disabled while the work is pending, no special
+ * action should be required to protect the work queue.
+ */
+
+ ret = work_queue(HPWORK, &priv->timeout, stmpe811_timeoutworker, priv, 0);
+ if (ret != 0)
+ {
+ illdbg("Failed to queue work: %d\n", ret);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: stmpe811_tscinitialize
+ *
+ * Description:
+ * Initialize the touchscreen controller. This is really a part of the
+ * stmpe811_register logic,
+ *
+ ****************************************************************************/
+
+static inline void stmpe811_tscinitialize(FAR struct stmpe811_dev_s *priv)
+{
+ uint8_t regval;
+
+ ivdbg("Initializing touchscreen controller\n");
+
+ /* Enable TSC and ADC functions */
+
+ regval = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2);
+ regval &= ~(SYS_CTRL2_TSC_OFF | SYS_CTRL2_ADC_OFF);
+ stmpe811_putreg8(priv, STMPE811_SYS_CTRL2, regval);
+
+ /* Enable the TSC global interrupts */
+
+ regval = stmpe811_getreg8(priv, STMPE811_INT_EN);
+ regval |= (uint32_t)(INT_TOUCH_DET | INT_FIFO_TH | INT_FIFO_OFLOW);
+ stmpe811_putreg8(priv, STMPE811_INT_EN, regval);
+
+ /* Select Sample Time, bit number and ADC Reference */
+
+ stmpe811_putreg8(priv, STMPE811_ADC_CTRL1, priv->config->ctrl1);
+
+ /* Wait for 20 ms */
+
+ up_mdelay(20);
+
+ /* Select the ADC clock speed */
+
+ stmpe811_putreg8(priv, STMPE811_ADC_CTRL2, priv->config->ctrl2);
+
+ /* Select TSC pins in non-GPIO mode (AF=0) */
+
+ regval = stmpe811_getreg8(priv, STMPE811_GPIO_AF);
+ regval &= ~(uint8_t)TSC_PIN_SET;
+ stmpe811_putreg8(priv, STMPE811_GPIO_AF, regval);
+
+ /* Select 2 nF filter capacitor */
+
+ stmpe811_putreg8(priv, STMPE811_TSC_CFG,
+ (TSC_CFG_AVE_CTRL_4SAMPLES | TSC_CFG_TOUCH_DELAY_500US | TSC_CFG_SETTLING_500US));
+
+ /* Select single point reading */
+
+ stmpe811_putreg8(priv, STMPE811_FIFO_TH, 1);
+
+ /* Reset and clear the FIFO. */
+
+ stmpe811_putreg8(priv, STMPE811_FIFO_STA, FIFO_STA_FIFO_RESET);
+ stmpe811_putreg8(priv, STMPE811_FIFO_STA, 0);
+
+ /* set the data format for Z value: 7 fractional part and 1 whole part */
+
+ stmpe811_putreg8(priv, STMPE811_TSC_FRACTIONZ, 0x01);
+
+ /* Set the driving capability of the device for TSC pins: 50mA */
+
+ stmpe811_putreg8(priv, STMPE811_TSC_IDRIVE, TSC_IDRIVE_50MA);
+
+ /* Enable the TSC. Use no tracking index, touch-screen controller
+ * operation mode (XYZ).
+ */
+
+ stmpe811_putreg8(priv, STMPE811_TSC_CTRL, TSC_CTRL_EN);
+
+ /* Clear all the status pending bits */
+
+ stmpe811_putreg8(priv, STMPE811_INT_STA, INT_ALL);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stmpe811_register
+ *
+ * Description:
+ * Enable TSC functionality. GPIO4-7 must be available. This function
+ * will register the touchsceen driver as /dev/inputN where N is the minor
+ * device number
+ *
+ * Input Parameters:
+ * handle - The handle previously returned by stmpe811_register
+ * minor - The input device minor number
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int stmpe811_register(STMPE811_HANDLE handle, int minor)
+{
+ FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle;
+ char devname[DEV_NAMELEN];
+ int ret;
+
+ ivdbg("handle=%p minor=%d\n", handle, minor);
+ DEBUGASSERT(priv);
+
+ /* Get exclusive access to the device structure */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0)
+ {
+ int errval = errno;
+ idbg("ERROR: sem_wait failed: %d\n", errval);
+ return -errval;
+ }
+
+ /* Make sure that the pins (4-7) need by the TSC are not already in use */
+
+ if ((priv->inuse & TSC_PIN_SET) != 0)
+ {
+ idbg("ERROR: TSC pins is already in-use: %02x\n", priv->inuse);
+ sem_post(&priv->exclsem);
+ return -EBUSY;
+ }
+
+ /* Initialize the TS structure fields to their default values */
+
+ priv->minor = minor;
+ priv->penchange = false;
+ priv->threshx = 0;
+ priv->threshy = 0;
+
+ /* Create a timer for catching missed pen up conditions */
+
+ priv->wdog = wd_create();
+ if (!priv->wdog)
+ {
+ idbg("ERROR: Failed to create a watchdog\n", errno);
+ sem_post(&priv->exclsem);
+ return -ENOSPC;
+ }
+
+ /* Register the character driver */
+
+ snprintf(devname, DEV_NAMELEN, DEV_FORMAT, minor);
+ ret = register_driver(devname, &g_stmpe811fops, 0666, priv);
+ if (ret < 0)
+ {
+ idbg("ERROR: Failed to register driver %s: %d\n", devname, ret);
+ sem_post(&priv->exclsem);
+ return ret;
+ }
+
+ /* Initialize the touchscreen controller */
+
+ stmpe811_tscinitialize(priv);
+
+ /* Inidicate that the touchscreen controller was successfully initialized */
+
+ priv->inuse |= TSC_PIN_SET; /* Pins 4-7 are now in-use */
+ priv->flags |= STMPE811_FLAGS_TSC_INITIALIZED; /* TSC function is initialized */
+ sem_post(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: stmpe811_tscworker
+ *
+ * Description:
+ * This function is called to handle a TSC interrupt. It is not really
+ * an interrupt handle because it is called from the STMPE811 "bottom half"
+ * logic that runs on the worker thread.
+ *
+ ****************************************************************************/
+
+void stmpe811_tscworker(FAR struct stmpe811_dev_s *priv, uint8_t intsta)
+{
+ FAR struct stmpe811_config_s *config; /* Convenience pointer */
+ bool pendown; /* true: pend is down */
+ uint16_t xdiff; /* X difference used in thresholding */
+ uint16_t ydiff; /* Y difference used in thresholding */
+ uint16_t x; /* X position */
+ uint16_t y; /* Y position */
+
+ ASSERT(priv != NULL);
+
+ /* Cancel the missing pen up timer */
+
+ (void)wd_cancel(priv->wdog);
+
+ /* Get a pointer the callbacks for convenience (and so the code is not so
+ * ugly).
+ */
+
+ config = priv->config;
+ DEBUGASSERT(config != NULL);
+
+ /* Check for pen up or down from the TSC_STA ibit n the STMPE811_TSC_CTRL register. */
+
+ pendown = (stmpe811_getreg8(priv, STMPE811_TSC_CTRL) & TSC_CTRL_TSC_STA) != 0;
+
+ /* Handle the change from pen down to pen up */
+
+ if (!pendown)
+ {
+ /* The pen is up.. reset thresholding variables. FIFOs will read zero if
+ * there is no data available (hence the choice of (0,0))
+ */
+
+ priv->threshx = 0;
+ priv->threshy = 0;
+
+ /* Ignore the interrupt if the pen was already up (CONTACT_NONE == pen up and
+ * already reported; CONTACT_UP == pen up, but not reported)
+ */
+
+ if (priv->sample.contact == CONTACT_NONE ||
+ priv->sample.contact == CONTACT_UP)
+ {
+ goto ignored;
+ }
+
+ /* A pen-down to up transition has been detected. CONTACT_UP indicates the
+ * initial loss of contzt. The state will be changed to CONTACT_NONE
+ * after the loss of contact is sampled.
+ */
+
+ priv->sample.contact = CONTACT_UP;
+ }
+
+ /* The pen is down... check for data in the FIFO */
+
+ else if ((intsta & (INT_FIFO_TH|INT_FIFO_OFLOW)) != 0)
+ {
+ /* Read the next x and y positions from the FIFO. */
+
+#ifdef CONFIG_STMPE811_SWAPXY
+ x = stmpe811_getreg16(priv, STMPE811_TSC_DATAX);
+ y = stmpe811_getreg16(priv, STMPE811_TSC_DATAY);
+#else
+ x = stmpe811_getreg16(priv, STMPE811_TSC_DATAY);
+ y = stmpe811_getreg16(priv, STMPE811_TSC_DATAX);
+#endif
+
+ /* If we have not yet processed the last pen up event, then we
+ * cannot handle this pen down event. We will have to discard it. That
+ * should be okay because there will be another FIFO event right behind
+ * this one. Other kinds of data overruns are not harmful.
+ *
+ * Hmm.. a better design might be to disable FIFO interrupts when we
+ * detect pen up. Then re-enable them when CONTACT_UP is reported.
+ * That would save processing interrupts just to discard the data.
+ */
+
+ if (priv->sample.contact == CONTACT_UP)
+ {
+ /* We have not closed the loop on the last touch ... don't report
+ * anything.
+ */
+
+ goto ignored;
+ }
+
+ /* Perform a thresholding operation so that the results will be more stable.
+ * If the difference from the last sample is small, then ignore the event.
+ * REVISIT: Should a large change in pressure also generate a event?
+ */
+
+ xdiff = x > priv->threshx ? (x - priv->threshx) : (priv->threshx - x);
+ ydiff = y > priv->threshy ? (y - priv->threshy) : (priv->threshy - y);
+
+ if (xdiff < CONFIG_STMPE811_THRESHX && ydiff < CONFIG_STMPE811_THRESHY)
+ {
+ /* Little or no change in either direction ... don't report anything. */
+
+ goto ignored;
+ }
+
+ /* When we see a big difference, snap to the new x/y thresholds */
+
+ priv->threshx = x;
+ priv->threshy = y;
+
+ /* Update the x/y position in the sample data */
+
+ priv->sample.x = priv->threshx;
+ priv->sample.y = priv->threshy;
+
+ /* Update the Z pressure index */
+
+ priv->sample.z = stmpe811_getreg8(priv, STMPE811_TSC_DATAZ);
+ priv->sample.valid = true;
+
+ /* If this is the first (acknowledged) pen down report, then report
+ * this as the first contact. If contact == CONTACT_DOWN, it will be
+ * set to set to CONTACT_MOVE after the contact is first sampled.
+ */
+
+ if (priv->sample.contact != CONTACT_MOVE)
+ {
+ /* First contact */
+
+ priv->sample.contact = CONTACT_DOWN;
+ }
+ }
+
+ /* Pen down, but no data in FIFO */
+
+ else
+ {
+ /* Ignore the interrupt... wait until there is data in the FIFO */
+
+ goto ignored;
+ }
+
+ /* We get here if (1) we just went from a pen down to a pen up state OR (2)
+ * We just get a measurement from the FIFO in a pen down state. Indicate
+ * the availability of new sample data for this ID.
+ */
+
+ priv->sample.id = priv->id;
+ priv->penchange = true;
+
+ /* Notify any waiters that new STMPE811 data is available */
+
+ stmpe811_notify(priv);
+
+ /* If we think that the pend is still down, the start/re-start the pen up
+ * timer.
+ */
+
+ignored:
+ if (priv->sample.contact == CONTACT_MOVE ||
+ priv->sample.contact == CONTACT_MOVE)
+ {
+ (void)wd_start(priv->wdog, STMPE811_PENUP_TICKS, stmpe811_timeout,
+ 1, (uint32_t)((uintptr_t)priv));
+ }
+
+ /* Reset and clear all data in the FIFO */
+
+ stmpe811_putreg8(priv, STMPE811_FIFO_STA, FIFO_STA_FIFO_RESET);
+ stmpe811_putreg8(priv, STMPE811_FIFO_STA, 0);
+}
+
+#endif /* CONFIG_INPUT && CONFIG_INPUT_STMPE811 && !CONFIG_STMPE811_TSC_DISABLE */
+
diff --git a/nuttx/drivers/input/tsc2007.c b/nuttx/drivers/input/tsc2007.c
new file mode 100644
index 000000000..163118b95
--- /dev/null
+++ b/nuttx/drivers/input/tsc2007.c
@@ -0,0 +1,1336 @@
+/****************************************************************************
+ * drivers/input/tsc2007.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "1.2V to 3.6V, 12-Bit, Nanopower, 4-Wire Micro TOUCH SCREEN CONTROLLER
+ * with I2C Interface," SBAS405A March 2007, Revised, March 2009, Texas
+ * Instruments Incorporated
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/* The TSC2007 is an analog interface circuit for a human interface touch
+ * screen device. All peripheral functions are controlled through the command
+ * byte and onboard state machines.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <poll.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/i2c.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/input/touchscreen.h>
+#include <nuttx/input/tsc2007.h>
+
+#include "tsc2007.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+/* Reference counting is partially implemented, but not needed in the
+ * current design.
+ */
+
+#undef CONFIG_TSC2007_REFCNT
+
+/* I don't think that it is necessary to activate the converters before
+ * making meaurements. However, I will keep this functionality enabled
+ * until I have a change to prove that that activation is unnecessary.
+ */
+
+#undef CONFIG_TSC2007_ACTIVATE
+#define CONFIG_TSC2007_ACTIVATE 1
+
+/* Driver support ***********************************************************/
+/* This format is used to construct the /dev/input[n] device driver path. It
+ * defined here so that it will be used consistently in all places.
+ */
+
+#define DEV_FORMAT "/dev/input%d"
+#define DEV_NAMELEN 16
+
+/* Commands *****************************************************************/
+
+#define TSC2007_SETUP (TSC2007_CMD_FUNC_SETUP)
+#ifdef CONFIG_TSC2007_8BIT
+# define TSC2007_ACTIVATE_Y (TSC2007_CMD_8BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_YON)
+# define TSC2007_MEASURE_Y (TSC2007_CMD_8BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_YPOS)
+# define TSC2007_ACTIVATE_X (TSC2007_CMD_8BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_XON)
+# define TSC2007_MEASURE_X (TSC2007_CMD_8BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_XPOS)
+# define TSC2007_ACTIVATE_Z (TSC2007_CMD_8BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_YXON)
+# define TSC2007_MEASURE_Z1 (TSC2007_CMD_8BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_Z1POS)
+# define TSC2007_MEASURE_Z2 (TSC2007_CMD_8BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_Z2POS)
+# define TSC2007_ENABLE_PENIRQ (TSC2007_CMD_8BIT | TSC2007_CMD_PWRDN_IRQEN)
+#else
+# define TSC2007_ACTIVATE_Y (TSC2007_CMD_12BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_YON)
+# define TSC2007_MEASURE_Y (TSC2007_CMD_12BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_YPOS)
+# define TSC2007_ACTIVATE_X (TSC2007_CMD_12BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_XON)
+# define TSC2007_MEASURE_X (TSC2007_CMD_12BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_XPOS)
+# define TSC2007_ACTIVATE_Z (TSC2007_CMD_12BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_YXON)
+# define TSC2007_MEASURE_Z1 (TSC2007_CMD_12BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_Z1POS)
+# define TSC2007_MEASURE_Z2 (TSC2007_CMD_12BIT | TSC2007_CMD_ADCON_IRQDIS | TSC2007_CMD_FUNC_Z2POS)
+# define TSC2007_ENABLE_PENIRQ (TSC2007_CMD_12BIT | TSC2007_CMD_PWRDN_IRQEN)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This describes the state of one contact */
+
+enum tsc2007_contact_3
+{
+ CONTACT_NONE = 0, /* No contact */
+ CONTACT_DOWN, /* First contact */
+ CONTACT_MOVE, /* Same contact, possibly different position */
+ CONTACT_UP, /* Contact lost */
+};
+
+/* This structure describes the results of one TSC2007 sample */
+
+struct tsc2007_sample_s
+{
+ uint8_t id; /* Sampled touch point ID */
+ uint8_t contact; /* Contact state (see enum tsc2007_contact_e) */
+ bool valid; /* True: x,y,pressure contain valid, sampled data */
+ uint16_t x; /* Measured X position */
+ uint16_t y; /* Measured Y position */
+ uint16_t pressure; /* Calculated pressure */
+};
+
+/* This structure describes the state of one TSC2007 driver instance */
+
+struct tsc2007_dev_s
+{
+#ifdef CONFIG_TSC2007_MULTIPLE
+ FAR struct tsc2007_dev_s *flink; /* Supports a singly linked list of drivers */
+#endif
+#ifdef CONFIG_TSC2007_REFCNT
+ uint8_t crefs; /* Number of times the device has been opened */
+#endif
+ uint8_t nwaiters; /* Number of threads waiting for TSC2007 data */
+ uint8_t id; /* Current touch point ID */
+ volatile bool penchange; /* An unreported event is buffered */
+ sem_t devsem; /* Manages exclusive access to this structure */
+ sem_t waitsem; /* Used to wait for the availability of data */
+
+ FAR struct tsc2007_config_s *config; /* Board configuration data */
+ FAR struct i2c_dev_s *i2c; /* Saved I2C driver instance */
+ struct work_s work; /* Supports the interrupt handling "bottom half" */
+ struct tsc2007_sample_s sample; /* Last sampled touch point data */
+
+ /* The following is a list if poll structures of threads waiting for
+ * driver events. The 'struct pollfd' reference for each open is also
+ * retained in the f_priv field of the 'struct file'.
+ */
+
+#ifndef CONFIG_DISABLE_POLL
+ struct pollfd *fds[CONFIG_TSC2007_NPOLLWAITERS];
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void tsc2007_notify(FAR struct tsc2007_dev_s *priv);
+static int tsc2007_sample(FAR struct tsc2007_dev_s *priv,
+ FAR struct tsc2007_sample_s *sample);
+static int tsc2007_waitsample(FAR struct tsc2007_dev_s *priv,
+ FAR struct tsc2007_sample_s *sample);
+#ifdef CONFIG_TSC2007_ACTIVATE
+static int tsc2007_activate(FAR struct tsc2007_dev_s *priv, uint8_t cmd);
+#endif
+static int tsc2007_transfer(FAR struct tsc2007_dev_s *priv, uint8_t cmd);
+static void tsc2007_worker(FAR void *arg);
+static int tsc2007_interrupt(int irq, FAR void *context);
+
+/* Character driver methods */
+
+static int tsc2007_open(FAR struct file *filep);
+static int tsc2007_close(FAR struct file *filep);
+static ssize_t tsc2007_read(FAR struct file *filep, FAR char *buffer, size_t len);
+static int tsc2007_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+#ifndef CONFIG_DISABLE_POLL
+static int tsc2007_poll(FAR struct file *filep, struct pollfd *fds, bool setup);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This the the vtable that supports the character driver interface */
+
+static const struct file_operations tsc2007_fops =
+{
+ tsc2007_open, /* open */
+ tsc2007_close, /* close */
+ tsc2007_read, /* read */
+ 0, /* write */
+ 0, /* seek */
+ tsc2007_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , tsc2007_poll /* poll */
+#endif
+};
+
+/* If only a single TSC2007 device is supported, then the driver state
+ * structure may as well be pre-allocated.
+ */
+
+#ifndef CONFIG_TSC2007_MULTIPLE
+static struct tsc2007_dev_s g_tsc2007;
+
+/* Otherwise, we will need to maintain allocated driver instances in a list */
+
+#else
+static struct tsc2007_dev_s *g_tsc2007list;
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tsc2007_notify
+ ****************************************************************************/
+
+static void tsc2007_notify(FAR struct tsc2007_dev_s *priv)
+{
+#ifndef CONFIG_DISABLE_POLL
+ int i;
+#endif
+
+ /* If there are threads waiting for read data, then signal one of them
+ * that the read data is available.
+ */
+
+ if (priv->nwaiters > 0)
+ {
+ /* After posting this semaphore, we need to exit because the TSC2007
+ * is no longer available.
+ */
+
+ sem_post(&priv->waitsem);
+ }
+
+ /* If there are threads waiting on poll() for TSC2007 data to become available,
+ * then wake them up now. NOTE: we wake up all waiting threads because we
+ * do not know that they are going to do. If they all try to read the data,
+ * then some make end up blocking after all.
+ */
+
+#ifndef CONFIG_DISABLE_POLL
+ for (i = 0; i < CONFIG_TSC2007_NPOLLWAITERS; i++)
+ {
+ struct pollfd *fds = priv->fds[i];
+ if (fds)
+ {
+ fds->revents |= POLLIN;
+ ivdbg("Report events: %02x\n", fds->revents);
+ sem_post(fds->sem);
+ }
+ }
+#endif
+}
+
+/****************************************************************************
+ * Name: tsc2007_sample
+ ****************************************************************************/
+
+static int tsc2007_sample(FAR struct tsc2007_dev_s *priv,
+ FAR struct tsc2007_sample_s *sample)
+{
+ irqstate_t flags;
+ int ret = -EAGAIN;
+
+ /* Interrupts me be disabled when this is called to (1) prevent posting
+ * of semphores from interrupt handlers, and (2) to prevent sampled data
+ * from changing until it has been reported.
+ */
+
+ flags = irqsave();
+
+ /* Is there new TSC2007 sample data available? */
+
+ if (priv->penchange)
+ {
+ /* Yes.. the state has changed in some way. Return a copy of the
+ * sampled data.
+ */
+
+ memcpy(sample, &priv->sample, sizeof(struct tsc2007_sample_s ));
+
+ /* Now manage state transitions */
+
+ if (sample->contact == CONTACT_UP)
+ {
+ /* Next.. no contact. Increment the ID so that next contact ID
+ * will be unique. X/Y positions are no longer valid.
+ */
+
+ priv->sample.contact = CONTACT_NONE;
+ priv->sample.valid = false;
+ priv->id++;
+ }
+ else if (sample->contact == CONTACT_DOWN)
+ {
+ /* First report -- next report will be a movement */
+
+ priv->sample.contact = CONTACT_MOVE;
+ }
+
+ priv->penchange = false;
+ ret = OK;
+ }
+
+ irqrestore(flags);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: tsc2007_waitsample
+ ****************************************************************************/
+
+static int tsc2007_waitsample(FAR struct tsc2007_dev_s *priv,
+ FAR struct tsc2007_sample_s *sample)
+{
+ irqstate_t flags;
+ int ret;
+
+ /* Interrupts me be disabled when this is called to (1) prevent posting
+ * of semphores from interrupt handlers, and (2) to prevent sampled data
+ * from changing until it has been reported.
+ *
+ * In addition, we will also disable pre-emption to prevent other threads
+ * from getting control while we muck with the semaphores.
+ */
+
+ sched_lock();
+ flags = irqsave();
+
+ /* Now release the semaphore that manages mutually exclusive access to
+ * the device structure. This may cause other tasks to become ready to
+ * run, but they cannot run yet because pre-emption is disabled.
+ */
+
+ sem_post(&priv->devsem);
+
+ /* Try to get the a sample... if we cannot, then wait on the semaphore
+ * that is posted when new sample data is availble.
+ */
+
+ while (tsc2007_sample(priv, sample) < 0)
+ {
+ /* Wait for a change in the TSC2007 state */
+
+ priv->nwaiters++;
+ ret = sem_wait(&priv->waitsem);
+ priv->nwaiters--;
+
+ if (ret < 0)
+ {
+ /* If we are awakened by a signal, then we need to return
+ * the failure now.
+ */
+
+ DEBUGASSERT(errno == EINTR);
+ ret = -EINTR;
+ goto errout;
+ }
+ }
+
+ /* Re-acquire the the semaphore that manages mutually exclusive access to
+ * the device structure. We may have to wait here. But we have our sample.
+ * Interrupts and pre-emption will be re-enabled while we wait.
+ */
+
+ ret = sem_wait(&priv->devsem);
+
+errout:
+ /* Then re-enable interrupts. We might get interrupt here and there
+ * could be a new sample. But no new threads will run because we still
+ * have pre-emption disabled.
+ */
+
+ irqrestore(flags);
+
+ /* Restore pre-emption. We might get suspended here but that is okay
+ * because we already have our sample. Note: this means that if there
+ * were two threads reading from the TSC2007 for some reason, the data
+ * might be read out of order.
+ */
+
+ sched_unlock();
+ return ret;
+}
+
+/****************************************************************************
+ * Name: tsc2007_activate
+ ****************************************************************************/
+
+#ifdef CONFIG_TSC2007_ACTIVATE
+static int tsc2007_activate(FAR struct tsc2007_dev_s *priv, uint8_t cmd)
+{
+ struct i2c_msg_s msg;
+ uint8_t data;
+ int ret;
+
+ /* Send the setup command (with no ACK) followed by the A/D converter
+ * activation command (ACKed).
+ */
+
+ data = TSC2007_SETUP;
+
+ msg.addr = priv->config->address; /* 7-bit address */
+ msg.flags = 0; /* Write transaction, beginning with START */
+ msg.buffer = &data; /* Transfer from this address */
+ msg.length = 1; /* Send one byte following the address */
+
+ /* Ignore errors from the setup command (because it is not ACKed) */
+
+ (void)I2C_TRANSFER(priv->i2c, &msg, 1);
+
+ /* Now activate the A/D converter */
+
+ data = cmd;
+
+ msg.addr = priv->config->address; /* 7-bit address */
+ msg.flags = 0; /* Write transaction, beginning with START */
+ msg.buffer = &data; /* Transfer from this address */
+ msg.length = 1; /* Send one byte following the address */
+
+ ret = I2C_TRANSFER(priv->i2c, &msg, 1);
+ if (ret < 0)
+ {
+ idbg("I2C_TRANSFER failed: %d\n", ret);
+ }
+ return ret;
+}
+#else
+# define tsc2007_activate(p,c)
+#endif
+
+/****************************************************************************
+ * Name: tsc2007_transfer
+ ****************************************************************************/
+
+static int tsc2007_transfer(FAR struct tsc2007_dev_s *priv, uint8_t cmd)
+{
+ struct i2c_msg_s msg;
+ uint8_t data12[2];
+ int ret;
+
+ /* "A conversion/write cycle begins when the master issues the address
+ * byte containing the slave address of the TSC2007, with the eighth bit
+ * equal to a 0 (R/W = 0)... Once the eighth bit has been received...
+ * the TSC2007 issues an acknowledge.
+ *
+ * "When the master receives the acknowledge bit from the TSC2007, the
+ * master writes the command byte to the slave... After the command byte
+ * is received by the slave, the slave issues another acknowledge bit.
+ * The master then ends the write cycle by issuing a repeated START or a
+ * STOP condition...
+ */
+
+ msg.addr = priv->config->address; /* 7-bit address */
+ msg.flags = 0; /* Write transaction, beginning with START */
+ msg.buffer = &cmd; /* Transfer from this address */
+ msg.length = 1; /* Send one byte following the address */
+
+ ret = I2C_TRANSFER(priv->i2c, &msg, 1);
+ if (ret < 0)
+ {
+ idbg("I2C_TRANSFER failed: %d\n", ret);
+ return ret;
+ }
+
+ /* "The input multiplexer channel for the A/D converter is selected when
+ * bits C3 through C0 are clocked in. If the selected channel is an X-,Y-,
+ * or Z-position measurement, the appropriate drivers turn on once the
+ * acquisition period begins.
+ *
+ * "... the input sample acquisition period starts on the falling edge of
+ * SCL when the C0 bit of the command byte has been latched, and ends
+ * when a STOP or repeated START condition has been issued. A/D conversion
+ * starts immediately after the acquisition period...
+ *
+ * "For best performance, the I2C bus should remain in an idle state while
+ * an A/D conversion is taking place. ... The master should wait for at
+ * least 10ms before attempting to read data from the TSC2007...
+ */
+
+ usleep(10*1000);
+
+ /* "Data access begins with the master issuing a START condition followed
+ * by the address byte ... with R/W = 1.
+ *
+ * "When the eighth bit has been received and the address matches, the
+ * slave issues an acknowledge. The first byte of serial data then follows
+ * (D11-D4, MSB first).
+ *
+ * "After the first byte has been sent by the slave, it releases the SDA line
+ * for the master to issue an acknowledge. The slave responds with the
+ * second byte of serial data upon receiving the acknowledge from the master
+ * (D3-D0, followed by four 0 bits). The second byte is followed by a NOT
+ * acknowledge bit (ACK = 1) from the master to indicate that the last
+ * data byte has been received...
+ */
+
+ msg.addr = priv->config->address; /* 7-bit address */
+ msg.flags = I2C_M_READ; /* Read transaction, beginning with START */
+ msg.buffer = data12; /* Transfer to this address */
+ msg.length = 2; /* Read two bytes following the address */
+
+ ret = I2C_TRANSFER(priv->i2c, &msg, 1);
+ if (ret < 0)
+ {
+ idbg("I2C_TRANSFER failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Get the MS 8 bits from the first byte and the remaining LS 4 bits from
+ * the second byte. The valid range of data is then from 0 to 4095 with
+ * the LSB unit corresponding to Vref/4096.
+ */
+
+ ret = (unsigned int)data12[0] << 4 | (unsigned int)data12[1] >> 4;
+ ivdbg("data: 0x%04x\n", ret);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: tsc2007_worker
+ ****************************************************************************/
+
+static void tsc2007_worker(FAR void *arg)
+{
+ FAR struct tsc2007_dev_s *priv = (FAR struct tsc2007_dev_s *)arg;
+ FAR struct tsc2007_config_s *config; /* Convenience pointer */
+ bool pendown; /* true: pend is down */
+ uint16_t x; /* X position */
+ uint16_t y; /* Y position */
+ uint16_t z1; /* Z1 position */
+ uint16_t z2; /* Z2 position */
+ uint32_t pressure; /* Measured pressure */
+
+ ASSERT(priv != NULL);
+
+ /* Get a pointer the callbacks for convenience (and so the code is not so
+ * ugly).
+ */
+
+ config = priv->config;
+ DEBUGASSERT(config != NULL);
+
+ /* Check for pen up or down by reading the PENIRQ GPIO. */
+
+ pendown = config->pendown(config);
+
+ /* Handle the change from pen down to pen up */
+
+ if (!pendown)
+ {
+ /* Ignore the interrupt if the pen was already down (CONTACT_NONE == pen up and
+ * already reported. CONTACT_UP == pen up, but not reported)
+ */
+
+ if (priv->sample.contact == CONTACT_NONE)
+ {
+ goto errout;
+ }
+ }
+
+ /* It is a pen down event. If the last loss-of-contact event has not been
+ * processed yet, then we have to ignore the pen down event (or else it will
+ * look like a drag event)
+ */
+
+ else if (priv->sample.contact == CONTACT_UP)
+ {
+ goto errout;
+ }
+ else
+ {
+ /* Handle all pen down events. First, sample X, Y, Z1, and Z2 values.
+ *
+ * "A resistive touch screen operates by applying a voltage across a
+ * resistor network and measuring the change in resistance at a given
+ * point on the matrix where the screen is touched by an input (stylus,
+ * pen, or finger). The change in the resistance ratio marks the location
+ * on the touch screen.
+ *
+ * "The 4-wire touch screen panel works by applying a voltage across the
+ * vertical or horizontal resistive network. The A/D converter converts
+ * the voltage measured at the point where the panel is touched. A measurement
+ * of the Y position of the pointing device is made by connecting the X+
+ * input to a data converter chip, turning on the Y+ and Y– drivers, and
+ * digitizing the voltage seen at the X+ input ..."
+ *
+ * "... it is recommended that whenever the host writes to the TSC2007, the
+ * master processor masks the interrupt associated to PENIRQ. This masking
+ * prevents false triggering of interrupts when the PENIRQ line is disabled
+ * in the cases previously listed."
+ */
+
+ (void)tsc2007_activate(priv, TSC2007_ACTIVATE_X);
+ y = tsc2007_transfer(priv, TSC2007_MEASURE_Y);
+
+
+ /* "Voltage is then applied to the other axis, and the A/D converter
+ * converts the voltage representing the X position on the screen. This
+ * process provides the X and Y coordinates to the associated processor."
+ */
+
+ (void)tsc2007_activate(priv, TSC2007_ACTIVATE_Y);
+ x = tsc2007_transfer(priv, TSC2007_MEASURE_X);
+
+ /* "... To determine pen or finger touch, the pressure of the touch must be
+ * determined. ... There are several different ways of performing this
+ * measurement. The TSC2007 supports two methods. The first method requires
+ * knowing the X-plate resistance, the measurement of the X-position, and two
+ * additional cross panel measurements (Z2 and Z1) of the touch screen."
+ *
+ * Rtouch = Rxplate * (X / 4096)* (Z2/Z1 - 1)
+ *
+ * "The second method requires knowing both the X-plate and Y-plate
+ * resistance, measurement of X-position and Y-position, and Z1 ..."
+ *
+ * Rtouch = Rxplate * (X / 4096) * (4096/Z1 - 1) - Ryplate * (1 - Y/4096)
+ *
+ * Read Z1 and Z2 values.
+ */
+
+ (void)tsc2007_activate(priv, TSC2007_ACTIVATE_Z);
+ z1 = tsc2007_transfer(priv, TSC2007_MEASURE_Z1);
+ (void)tsc2007_activate(priv, TSC2007_ACTIVATE_Z);
+ z2 = tsc2007_transfer(priv, TSC2007_MEASURE_Z2);
+
+ /* Power down ADC and enable PENIRQ */
+
+ (void)tsc2007_transfer(priv, TSC2007_ENABLE_PENIRQ);
+
+ /* Now calculate the pressure using the first method, reduced to:
+ *
+ * Rtouch = X * Rxplate *(Z2 - Z1) * / Z1 / 4096
+ */
+
+ if (z1 == 0)
+ {
+ idbg("Z1 zero\n");
+ pressure = 0;
+ }
+ else
+ {
+ pressure = (x * config->rxplate * (z2 - z1)) / z1;
+ pressure = (pressure + 2048) >> 12;
+
+ ivdbg("Position: (%d,%4d) pressure: %u z1/2: (%d,%d)\n",
+ x, y, pressure, z1, z2);
+
+ /* Ignore out of range caculcations */
+
+ if (pressure > 0x0fff)
+ {
+ idbg("Dropped out-of-range pressure: %d\n", pressure);
+ pressure = 0;
+ }
+ }
+
+ /* Save the measurements */
+
+ priv->sample.x = x;
+ priv->sample.y = y;
+ priv->sample.pressure = pressure;
+ priv->sample.valid = true;
+ }
+
+ /* Note the availability of new measurements */
+
+ if (pendown)
+ {
+ /* If this is the first (acknowledged) pend down report, then report
+ * this as the first contact. If contact == CONTACT_DOWN, it will be
+ * set to set to CONTACT_MOVE after the contact is first sampled.
+ */
+
+ if (priv->sample.contact != CONTACT_MOVE)
+ {
+ /* First contact */
+
+ priv->sample.contact = CONTACT_DOWN;
+ }
+ }
+ else /* if (priv->sample.contact != CONTACT_NONE) */
+ {
+ /* The pen is up. NOTE: We know from a previous test, that this is a
+ * loss of contact condition. This will be changed to CONTACT_NONE
+ * after the loss of contact is sampled.
+ */
+
+ priv->sample.contact = CONTACT_UP;
+ }
+
+ /* Indicate the availability of new sample data for this ID */
+
+ priv->sample.id = priv->id;
+ priv->penchange = true;
+
+ /* Notify any waiters that nes TSC2007 data is available */
+
+ tsc2007_notify(priv);
+
+ /* Exit, re-enabling TSC2007 interrupts */
+
+errout:
+ config->enable(config, true);
+}
+
+/****************************************************************************
+ * Name: tsc2007_interrupt
+ ****************************************************************************/
+
+static int tsc2007_interrupt(int irq, FAR void *context)
+{
+ FAR struct tsc2007_dev_s *priv;
+ FAR struct tsc2007_config_s *config;
+ int ret;
+
+ /* Which TSC2007 device caused the interrupt? */
+
+#ifndef CONFIG_TSC2007_MULTIPLE
+ priv = &g_tsc2007;
+#else
+ for (priv = g_tsc2007list;
+ priv && priv->configs->irq != irq;
+ priv = priv->flink);
+
+ ASSERT(priv != NULL);
+#endif
+
+ /* Get a pointer the callbacks for convenience (and so the code is not so
+ * ugly).
+ */
+
+ config = priv->config;
+ DEBUGASSERT(config != NULL);
+
+ /* Disable further interrupts */
+
+ config->enable(config, false);
+
+ /* Transfer processing to the worker thread. Since TSC2007 interrupts are
+ * disabled while the work is pending, no special action should be required
+ * to protected the work queue.
+ */
+
+ DEBUGASSERT(priv->work.worker == NULL);
+ ret = work_queue(HPWORK, &priv->work, tsc2007_worker, priv, 0);
+ if (ret != 0)
+ {
+ illdbg("Failed to queue work: %d\n", ret);
+ }
+
+ /* Clear any pending interrupts and return success */
+
+ config->clear(config);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: tsc2007_open
+ ****************************************************************************/
+
+static int tsc2007_open(FAR struct file *filep)
+{
+#ifdef CONFIG_TSC2007_REFCNT
+ FAR struct inode *inode;
+ FAR struct tsc2007_dev_s *priv;
+ uint8_t tmp;
+ int ret;
+
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct tsc2007_dev_s *)inode->i_private;
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->devsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Increment the reference count */
+
+ tmp = priv->crefs + 1;
+ if (tmp == 0)
+ {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ goto errout_with_sem;
+ }
+
+ /* When the reference increments to 1, this is the first open event
+ * on the driver.. and an opportunity to do any one-time initialization.
+ */
+
+ /* Save the new open count on success */
+
+ priv->crefs = tmp;
+
+errout_with_sem:
+ sem_post(&priv->devsem);
+ return ret;
+#else
+ return OK;
+#endif
+}
+
+/****************************************************************************
+ * Name: tsc2007_close
+ ****************************************************************************/
+
+static int tsc2007_close(FAR struct file *filep)
+{
+#ifdef CONFIG_TSC2007_REFCNT
+ FAR struct inode *inode;
+ FAR struct tsc2007_dev_s *priv;
+ int ret;
+
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct tsc2007_dev_s *)inode->i_private;
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->devsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Decrement the reference count unless it would decrement a negative
+ * value. When the count decrements to zero, there are no further
+ * open references to the driver.
+ */
+
+ if (priv->crefs >= 1)
+ {
+ priv->crefs--;
+ }
+
+ sem_post(&priv->devsem);
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Name: tsc2007_read
+ ****************************************************************************/
+
+static ssize_t tsc2007_read(FAR struct file *filep, FAR char *buffer, size_t len)
+{
+ FAR struct inode *inode;
+ FAR struct tsc2007_dev_s *priv;
+ FAR struct touch_sample_s *report;
+ struct tsc2007_sample_s sample;
+ int ret;
+
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct tsc2007_dev_s *)inode->i_private;
+
+ /* Verify that the caller has provided a buffer large enough to receive
+ * the touch data.
+ */
+
+ if (len < SIZEOF_TOUCH_SAMPLE_S(1))
+ {
+ /* We could provide logic to break up a touch report into segments and
+ * handle smaller reads... but why?
+ */
+
+ return -ENOSYS;
+ }
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->devsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Try to read sample data. */
+
+ ret = tsc2007_sample(priv, &sample);
+ if (ret < 0)
+ {
+ /* Sample data is not available now. We would ave to wait to get
+ * receive sample data. If the user has specified the O_NONBLOCK
+ * option, then just return an error.
+ */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ ret = -EAGAIN;
+ goto errout;
+ }
+
+ /* Wait for sample data */
+
+ ret = tsc2007_waitsample(priv, &sample);
+ if (ret < 0)
+ {
+ /* We might have been awakened by a signal */
+
+ goto errout;
+ }
+ }
+
+ /* In any event, we now have sampled TSC2007 data that we can report
+ * to the caller.
+ */
+
+ report = (FAR struct touch_sample_s *)buffer;
+ memset(report, 0, SIZEOF_TOUCH_SAMPLE_S(1));
+ report->npoints = 1;
+ report->point[0].id = priv->id;
+ report->point[0].x = sample.x;
+ report->point[0].y = sample.y;
+ report->point[0].pressure = sample.pressure;
+
+ /* Report the appropriate flags */
+
+ if (sample.contact == CONTACT_UP)
+ {
+ /* Pen is now up. Is the positional data valid? This is important to
+ * know because the release will be sent to the window based on its
+ * last positional data.
+ */
+
+ if (sample.valid)
+ {
+ report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID |
+ TOUCH_POS_VALID | TOUCH_PRESSURE_VALID;
+ }
+ else
+ {
+ report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID;
+ }
+ }
+ else
+ {
+ if (sample.contact == CONTACT_DOWN)
+ {
+ /* First contact */
+
+ report->point[0].flags = TOUCH_DOWN | TOUCH_ID_VALID | TOUCH_POS_VALID;
+ }
+ else /* if (sample->contact == CONTACT_MOVE) */
+ {
+ /* Movement of the same contact */
+
+ report->point[0].flags = TOUCH_MOVE | TOUCH_ID_VALID | TOUCH_POS_VALID;
+ }
+
+ /* A pressure measurement of zero means that pressure is not available */
+
+ if (report->point[0].pressure != 0)
+ {
+ report->point[0].flags |= TOUCH_PRESSURE_VALID;
+ }
+ }
+
+ ret = SIZEOF_TOUCH_SAMPLE_S(1);
+
+errout:
+ sem_post(&priv->devsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name:tsc2007_ioctl
+ ****************************************************************************/
+
+static int tsc2007_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode;
+ FAR struct tsc2007_dev_s *priv;
+ int ret;
+
+ ivdbg("cmd: %d arg: %ld\n", cmd, arg);
+ DEBUGASSERT(filep);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct tsc2007_dev_s *)inode->i_private;
+
+ /* Get exclusive access to the driver data structure */
+
+ ret = sem_wait(&priv->devsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ /* Process the IOCTL by command */
+
+ switch (cmd)
+ {
+ case TSIOC_SETCALIB: /* arg: Pointer to int calibration value */
+ {
+ FAR int *ptr = (FAR int *)((uintptr_t)arg);
+ DEBUGASSERT(priv->config != NULL && ptr != NULL);
+ priv->config->rxplate = *ptr;
+ }
+ break;
+
+ case TSIOC_GETCALIB: /* arg: Pointer to int calibration value */
+ {
+ FAR int *ptr = (FAR int *)((uintptr_t)arg);
+ DEBUGASSERT(priv->config != NULL && ptr != NULL);
+ *ptr = priv->config->rxplate;
+ }
+ break;
+
+ case TSIOC_SETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
+ {
+ FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
+ DEBUGASSERT(priv->config != NULL && ptr != NULL);
+ priv->config->frequency = I2C_SETFREQUENCY(priv->i2c, *ptr);
+ }
+ break;
+
+ case TSIOC_GETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
+ {
+ FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
+ DEBUGASSERT(priv->config != NULL && ptr != NULL);
+ *ptr = priv->config->frequency;
+ }
+ break;
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ sem_post(&priv->devsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: tsc2007_poll
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static int tsc2007_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup)
+{
+ FAR struct inode *inode;
+ FAR struct tsc2007_dev_s *priv;
+ int ret;
+ int i;
+
+ ivdbg("setup: %d\n", (int)setup);
+ DEBUGASSERT(filep && fds);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct tsc2007_dev_s *)inode->i_private;
+
+ /* Are we setting up the poll? Or tearing it down? */
+
+ ret = sem_wait(&priv->devsem);
+ if (ret < 0)
+ {
+ /* This should only happen if the wait was canceled by an signal */
+
+ DEBUGASSERT(errno == EINTR);
+ return -EINTR;
+ }
+
+ if (setup)
+ {
+ /* Ignore waits that do not include POLLIN */
+
+ if ((fds->events & POLLIN) == 0)
+ {
+ idbg("Missing POLLIN: revents: %08x\n", fds->revents);
+ ret = -EDEADLK;
+ goto errout;
+ }
+
+ /* This is a request to set up the poll. Find an available
+ * slot for the poll structure reference
+ */
+
+ for (i = 0; i < CONFIG_TSC2007_NPOLLWAITERS; i++)
+ {
+ /* Find an available slot */
+
+ if (!priv->fds[i])
+ {
+ /* Bind the poll structure and this slot */
+
+ priv->fds[i] = fds;
+ fds->priv = &priv->fds[i];
+ break;
+ }
+ }
+
+ if (i >= CONFIG_TSC2007_NPOLLWAITERS)
+ {
+ idbg("No availabled slot found: %d\n", i);
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto errout;
+ }
+
+ /* Should we immediately notify on any of the requested events? */
+
+ if (priv->penchange)
+ {
+ tsc2007_notify(priv);
+ }
+ }
+ else if (fds->priv)
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+ DEBUGASSERT(slot != NULL);
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+errout:
+ sem_post(&priv->devsem);
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tsc2007_register
+ *
+ * Description:
+ * Configure the TSC2007 to use the provided I2C device instance. This
+ * will register the driver as /dev/inputN where N is the minor device
+ * number
+ *
+ * Input Parameters:
+ * dev - An I2C driver instance
+ * config - Persistant board configuration data
+ * minor - The input device minor number
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int tsc2007_register(FAR struct i2c_dev_s *dev,
+ FAR struct tsc2007_config_s *config, int minor)
+{
+ FAR struct tsc2007_dev_s *priv;
+ char devname[DEV_NAMELEN];
+#ifdef CONFIG_TSC2007_MULTIPLE
+ irqstate_t flags;
+#endif
+ int ret;
+
+ ivdbg("dev: %p minor: %d\n", dev, minor);
+
+ /* Debug-only sanity checks */
+
+ DEBUGASSERT(dev != NULL && config != NULL && minor >= 0 && minor < 100);
+ DEBUGASSERT((config->address & 0xfc) == 0x48);
+ DEBUGASSERT(config->attach != NULL && config->enable != NULL &&
+ config->clear != NULL && config->pendown != NULL);
+
+ /* Create and initialize a TSC2007 device driver instance */
+
+#ifndef CONFIG_TSC2007_MULTIPLE
+ priv = &g_tsc2007;
+#else
+ priv = (FAR struct tsc2007_dev_s *)kmalloc(sizeof(struct tsc2007_dev_s));
+ if (!priv)
+ {
+ idbg("kmalloc(%d) failed\n", sizeof(struct tsc2007_dev_s));
+ return -ENOMEM;
+ }
+#endif
+
+ /* Initialize the TSC2007 device driver instance */
+
+ memset(priv, 0, sizeof(struct tsc2007_dev_s));
+ priv->i2c = dev; /* Save the I2C device handle */
+ priv->config = config; /* Save the board configuration */
+ sem_init(&priv->devsem, 0, 1); /* Initialize device structure semaphore */
+ sem_init(&priv->waitsem, 0, 0); /* Initialize pen event wait semaphore */
+
+ /* Set the I2C frequency (saving the actual frequency) */
+
+ config->frequency = I2C_SETFREQUENCY(dev, config->frequency);
+
+ /* Set the I2C address and address size */
+
+ ret = I2C_SETADDRESS(dev, config->address, 7);
+ if (ret < 0)
+ {
+ idbg("I2C_SETADDRESS failed: %d\n", ret);
+ goto errout_with_priv;
+ }
+
+ /* Make sure that interrupts are disabled */
+
+ config->clear(config);
+ config->enable(config, false);
+
+ /* Attach the interrupt handler */
+
+ ret = config->attach(config, tsc2007_interrupt);
+ if (ret < 0)
+ {
+ idbg("Failed to attach interrupt\n");
+ goto errout_with_priv;
+ }
+
+ /* Power down the ADC and enable PENIRQ. This is the normal state while
+ * waiting for a touch event.
+ */
+
+ ret = tsc2007_transfer(priv, TSC2007_ENABLE_PENIRQ);
+ if (ret < 0)
+ {
+ idbg("tsc2007_transfer failed: %d\n", ret);
+ goto errout_with_priv;
+ }
+
+ /* Register the device as an input device */
+
+ (void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, minor);
+ ivdbg("Registering %s\n", devname);
+
+ ret = register_driver(devname, &tsc2007_fops, 0666, priv);
+ if (ret < 0)
+ {
+ idbg("register_driver() failed: %d\n", ret);
+ goto errout_with_priv;
+ }
+
+ /* If multiple TSC2007 devices are supported, then we will need to add
+ * this new instance to a list of device instances so that it can be
+ * found by the interrupt handler based on the recieved IRQ number.
+ */
+
+#ifdef CONFIG_TSC2007_MULTIPLE
+ flags = irqsave();
+ priv->flink = g_tsc2007list;
+ g_tsc2007list = priv;
+ irqrestore(flags);
+#endif
+
+ /* Schedule work to perform the initial sampling and to set the data
+ * availability conditions.
+ */
+
+ ret = work_queue(HPWORK, &priv->work, tsc2007_worker, priv, 0);
+ if (ret != 0)
+ {
+ idbg("Failed to queue work: %d\n", ret);
+ goto errout_with_priv;
+ }
+
+ /* And return success (?) */
+
+ return OK;
+
+errout_with_priv:
+ sem_destroy(&priv->devsem);
+#ifdef CONFIG_TSC2007_MULTIPLE
+ kfree(priv);
+#endif
+ return ret;
+}
diff --git a/nuttx/drivers/input/tsc2007.h b/nuttx/drivers/input/tsc2007.h
new file mode 100644
index 000000000..76d5962bf
--- /dev/null
+++ b/nuttx/drivers/input/tsc2007.h
@@ -0,0 +1,120 @@
+/********************************************************************************************
+ * drivers/input/tsc2007.h
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "1.2V to 3.6V, 12-Bit, Nanopower, 4-Wire Micro TOUCH SCREEN CONTROLLER
+ * with I2C Interface," SBAS405A March 2007, Revised, March 2009, Texas
+ * Instruments Incorporated
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ********************************************************************************************/
+
+/* The TSC2007 is an analog interface circuit for a human interface touch screen device.
+ * All peripheral functions are controlled through the command byte and onboard state
+ * machines.
+ */
+
+#ifndef __DRIVERS_INPUT_TSC2007_H
+#define __DRIVERS_INPUT_TSC2007_H
+
+/********************************************************************************************
+ * Included Files
+ ********************************************************************************************/
+
+/********************************************************************************************
+ * Pre-Processor Definitions
+ ********************************************************************************************/
+
+/* TSC2007 Address */
+
+#define TSC2007_ADDRESS_MASK (0xf8) /* Bits 3-7: Invariant part of TSC2007 address */
+#define TSC2007_ADDRESS (0x90) /* Bits 3-7: Always set at '10010' */
+#define TSC2007_A1 (1 << 2) /* Bit 2: A1 */
+#define TSC2007_A0 (1 << 1) /* Bit 1: A1 */
+#define TSC2007_READ (1 << 0) /* Bit0=1: Selects read operation */
+#define TSC2007_WRITE (0) /* Bit0=0: Selects write operation */
+
+/* TSC2007 Command Byte */
+
+#define TSC2007_CMD_FUNC_SHIFT (4) /* Bits 4-7: Converter function select bits */
+#define TSC2007_CMD_FUNC_MASK (15 << TSC2007_CMD_FUNC_SHIFT)
+# define TSC2007_CMD_FUNC_TEMP0 (0 << TSC2007_CMD_FUNC_SHIFT) /* Measure TEMP0 */
+# define TSC2007_CMD_FUNC_AUX (2 << TSC2007_CMD_FUNC_SHIFT) /* Measure AUX */
+# define TSC2007_CMD_FUNC_TEMP1 (4 << TSC2007_CMD_FUNC_SHIFT) /* Measure TEMP1 */
+# define TSC2007_CMD_FUNC_XON (8 << TSC2007_CMD_FUNC_SHIFT) /* Activate X-drivers */
+# define TSC2007_CMD_FUNC_YON (9 << TSC2007_CMD_FUNC_SHIFT) /* Activate Y-drivers */
+# define TSC2007_CMD_FUNC_YXON (10 << TSC2007_CMD_FUNC_SHIFT) /* Activate Y+, X-drivers */
+# define TSC2007_CMD_FUNC_SETUP (11 << TSC2007_CMD_FUNC_SHIFT) /* Setup command */
+# define TSC2007_CMD_FUNC_XPOS (12 << TSC2007_CMD_FUNC_SHIFT) /* Measure X position */
+# define TSC2007_CMD_FUNC_YPOS (13 << TSC2007_CMD_FUNC_SHIFT) /* Measure Y position */
+# define TSC2007_CMD_FUNC_Z1POS (14 << TSC2007_CMD_FUNC_SHIFT) /* Measure Z1 position */
+# define TSC2007_CMD_FUNC_Z2POS (15 << TSC2007_CMD_FUNC_SHIFT) /* Measure Z2 positionn */
+#define TSC2007_CMD_PWRDN_SHIFT (2) /* Bits 2-3: Power-down bits */
+#define TSC2007_CMD_PWRDN_MASK (3 << TSC2007_CMD_PWRDN_SHIFT)
+# define TSC2007_CMD_PWRDN_IRQEN (0 << TSC2007_CMD_PWRDN_SHIFT) /* 00: Power down between cycles; PENIRQ enabled */
+# define TSC2007_CMD_ADCON_IRQDIS (1 << TSC2007_CMD_PWRDN_SHIFT) /* 01: A/D converter on; PENIRQ disabled */
+# define TSC2007_CMD_ADCOFF_IRQEN (2 << TSC2007_CMD_PWRDN_SHIFT) /* 10: A/D converter off; PENIRQ enabled. */
+ /* 11: A/D converter on; PENIRQ disabled. */
+#define TSC2007_CMD_12BIT (0) /* Bit 1: 0=12-bit */
+#define TSC2007_CMD_8BIT (1 << 1) /* Bit 1: 1=8-bit */
+ /* Bit 0: Don't care */
+
+/* TSC2007 Setup Command */
+
+#define TSC2007_SETUP_CMD TSC2007_CMD_FUNC_SETUP /* Bits 4-7: Setup command */
+ /* Bits 2-3: Must be zero */
+#define TSC2007_CMD_USEMAV (0) /* Bit 1: 0: Use the onboard MAV filter (default) */
+#define TSC2007_CMD_BYPASSMAV (1 << 1) /* Bit 1: 1: Bypass the onboard MAV filter */
+#define TSC2007_CMD_PU_50KOHM (0) /* Bit 0: 0: RIRQ = 50kOhm (default). */
+#define TSC2007_CMD_PU_90KOHM (1 << 1) /* Bit 0: 1: 1: RIRQ = 90kOhm */
+
+/********************************************************************************************
+ * Public Types
+ ********************************************************************************************/
+
+/********************************************************************************************
+ * Public Function Prototypes
+ ********************************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRIVERS_INPUT_TSC2007_H */
diff --git a/nuttx/drivers/lcd/Kconfig b/nuttx/drivers/lcd/Kconfig
new file mode 100644
index 000000000..081a79c89
--- /dev/null
+++ b/nuttx/drivers/lcd/Kconfig
@@ -0,0 +1,214 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+config LCD_MAXCONTRAST
+ int "LCD maximum contrast"
+ default 63 if NOKIA6100_S1D15G10
+ default 127 if NOKIA6100_PCF8833
+ default 255 if LCD_P14201
+ default 63
+ ---help---
+ must be 63 with the Epson controller and 127 with
+ the Phillips controller.
+
+config LCD_MAXPOWER
+ int "LCD maximum power"
+ default 1
+ ---help---
+ Maximum value of backlight setting. The backlight
+ control is managed outside of the 6100 driver so this value has no
+ meaning to the driver. Board-specific logic may place restrictions on
+ this value.
+
+config LCD_P14201
+ bool "Rit P1402 series display"
+ default n
+ ---help---
+ p14201.c. Driver for RiT P14201 series display with SD1329 IC
+ controller. This OLED is used with older versions of the
+ TI/Luminary LM3S8962 Evaluation Kit.
+if LCD_P14201
+config P14201_NINTERFACES
+ int "Number of physical P14201 devices"
+ default 1
+ range 1,1
+ ---help---
+ Specifies the number of physical P14201
+ devices that will be supported.
+
+config P14201_SPIMODE
+ int "SPI mode"
+ default 2
+ range 0,3
+ ---help---
+ Controls the SPI mode
+
+config P14201_FREQUENCY
+ int "SPI frequency"
+ default 1000000
+ ---help---
+ Define to use a different bus frequency,FIXME DEFAULT VALUE OK?
+
+config P14201_FRAMEBUFFER
+ bool "Enable P14201 GDDRAM cache"
+ default y
+ ---help---
+ If defined, accesses will be performed
+ using an in-memory copy of the OLEDs GDDRAM. This cost of this
+ buffer is 128 * 96 / 2 = 6Kb. If this is defined, then the driver
+ will be fully functional. If not, then it will have the following
+ limitations:
+
+ Reading graphics memory cannot be supported, and
+
+ All pixel writes must be aligned to byte boundaries.
+ The latter limitation effectively reduces the 128x96 disply to 64x96.
+endif
+
+config LCD_NOKIA6100
+ bool "Nokia 6100 display support"
+ default n
+ ---help---
+ nokia6100.c. Supports the Nokia 6100 display with either the Philips
+ PCF883 or the Epson S1D15G10 display controller. This LCD is used
+ with the Olimex LPC1766-STK (but has not been fully integrated).
+if LCD_NOKIA6100
+config NOKIA6100_NINTERFACES
+ int "Number of physical NOKIA6100 devices"
+ default 1
+ range 1,1
+ ---help---
+ Specifies the number of physical Nokia
+ 6100 devices that will be supported.
+
+choice NOKIA6100_CONTROLLER
+ prompt "Controller Setup"
+ default NOKIA6100_S1D15G10
+config NOKIA6100_S1D15G10
+ bool "S1D15G10 controller"
+ ---help---
+ Selects the Epson S1D15G10 display controller
+
+config NOKIA6100_PCF8833
+ bool "PCF8833 controller"
+ ---help---
+ Selects the Phillips PCF8833 display controller
+endchoice
+
+config NOKIA6100_SPIMODE
+ int "SPI mode"
+ default 0
+ range 0,3
+ ---help---
+ Controls the SPI mode
+
+config NOKIA6100_FREQUENCY
+ int "SPI frequency"
+ default 1000000
+ ---help---
+ Define to use a different bus frequency
+
+config NOKIA6100_BLINIT
+ bool "Back light initial"
+ default n
+ ---help---
+ Initial backlight setting
+ The following may need to be tuned for your hardware:
+
+config NOKIA6100_BPP
+ int "Display bits per pixel"
+ default 8
+ ---help---
+ Device supports 8, 12, and 16 bits per pixel.
+
+config NOKIA6100_INVERT
+ int "Display inversion"
+ default 1
+ range 0,1
+ ---help---
+ Display inversion, 0 or 1, Default: 1
+
+config NOKIA6100_MY
+ int "Display row direction"
+ default 0
+ range 0,1
+ ---help---
+ Display row direction, 0 or 1, Default: 0
+
+config NOKIA6100_MX
+ int "Display column direction"
+ default 1
+ range 0,1
+ ---help---
+ Display column direction, 0 or 1, Default: 1
+
+config NOKIA6100_V
+ int "Display address direction"
+ default 0
+ range 0,1
+ ---help---
+ Display address direction, 0 or 1, Default: 0
+
+config NOKIA6100_ML
+ int "Display scan direction"
+ default 0
+ range 0,1
+ ---help---
+ Display scan direction, 0 or 1, Default: 0
+
+config NOKIA6100_RGBORD
+ int "Display RGB order"
+ default 0
+ range 0,1
+ ---help---
+ Display RGB order, 0 or 1, Default: 0
+ Required LCD driver settings:
+endif
+
+config LCD_UG9664HSWAG01
+ bool "9664HSWAG01 OLED Display Module"
+ default n
+ ---help---
+ ug-9664hswag01.c. OLED Display Module, UG-9664HSWAG01", Univision
+ Technology Inc. Used with the LPC Xpresso and Embedded Artists
+ base board.
+
+choice
+ prompt "LCD Orientation"
+ default LCD_LANDSCAPE
+ depends on LCD
+ ---help---
+ Some LCD drivers may support displays in different orientations.
+ If the LCD driver supports this capability, than these are configuration
+ options to select that display orientation.
+
+config LCD_LANDSCAPE
+ bool "Landscape orientation"
+ ---help---
+ Define for "landscape" orientation support. Landscape mode refers one
+ of two orientations where the the display is wider than it is tall
+ (LCD_RLANDSCAPE is the other). This is the default orientation.
+
+config LCD_PORTRAIT
+ bool "Portrait orientation"
+ ---help---
+ Define for "portrait" orientation support. Portrait mode refers one
+ of two orientations where the the display is taller than it is wide
+ (LCD_RPORTAIT is the other).
+
+config LCD_RPORTRAIT
+ bool "Reverse portrait display"
+ ---help---
+ Define for "reverse portrait" orientation support. Reverse portrait mode
+ refers one of two orientations where the the display is taller than it is
+ wide (LCD_PORTAIT is the other).
+
+config LCD_RLANDSCAPE
+ bool "Reverse landscape orientation"
+ ---help---
+ Define for "reverse landscape" orientation support. Reverse landscape mode
+ refers one of two orientations where the the display is wider than it is
+ tall (LCD_LANDSCAPE is the other).
+
+endchoice
diff --git a/nuttx/drivers/lcd/Make.defs b/nuttx/drivers/lcd/Make.defs
new file mode 100644
index 000000000..26b169391
--- /dev/null
+++ b/nuttx/drivers/lcd/Make.defs
@@ -0,0 +1,68 @@
+############################################################################
+# drivers/lcd/Make.defs
+#
+# Copyright (C) 2010-2012 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+# Don't build anything if there is no NX support for LCD drivers
+
+ifeq ($(CONFIG_NX_LCDDRIVER),y)
+
+# Include LCD drivers
+
+ifeq ($(CONFIG_LCD_P14201),y)
+ CSRCS += p14201.c
+endif
+
+ifeq ($(CONFIG_LCD_NOKIA6100),y)
+ CSRCS += nokia6100.c
+endif
+
+ifeq ($(CONFIG_LCD_UG9664HSWAG01),y)
+ CSRCS += ug-9664hswag01.c
+endif
+
+ifeq ($(CONFIG_LCD_SSD1289),y)
+ CSRCS += ssd1289.c
+endif
+
+ifeq ($(CONFIG_LCD_MIO283QT2),y)
+ CSRCS += mio283qt2.c
+endif
+
+# Include LCD driver build support
+
+DEPPATH += --dep-path lcd
+VPATH += :lcd
+CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/lcd}
+endif
+
diff --git a/nuttx/drivers/lcd/README.txt b/nuttx/drivers/lcd/README.txt
new file mode 100644
index 000000000..77ae536b2
--- /dev/null
+++ b/nuttx/drivers/lcd/README.txt
@@ -0,0 +1,165 @@
+nuttx/drivers/lcd README
+========================
+
+This is the README.txt file for the drivers/lcd/ directory.
+
+Contents
+========
+
+ - LCD Header files
+ include/nuttx/lcd/lcd.h
+ struct lcd_dev_s
+ - Binding LCD Drivers
+ - Examples: /drivers/lcd/
+ - Examples: configs/
+ - graphics/
+
+LCD Header files
+================
+
+ include/nuttx/lcd/lcd.h
+
+ Structures and APIs needed to work with LCD drivers are provided in
+ this header file. This header file also depends on some of the same
+ definitions used for the frame buffer driver as privided in
+ include/nuttx/fb.h.
+
+ struct lcd_dev_s
+
+ Each LCD device driver must implement an instance of struct lcd_dev_s.
+ That structure defines a call table with the following methods:
+
+ - Get information about the LCD video controller configuration and the
+ configuration of each LCD color plane.
+
+ int (*getvideoinfo)(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo);
+ int (*getplaneinfo)(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo);
+
+ - The following are provided only if the video hardware supports RGB
+ color mapping:
+
+ int (*getcmap)(FAR struct lcd_dev_s *dev,
+ FAR struct fb_cmap_s *cmap);
+ int (*putcmap)(FAR struct lcd_dev_s *dev,
+ FAR const struct fb_cmap_s *cmap);
+
+ - The following are provided only if the video hardware supports a
+ hardware cursor:
+
+ int (*getcursor)(FAR struct lcd_dev_s *dev,
+ FAR struct fb_cursorattrib_s *attrib);
+ int (*setcursor)(FAR struct lcd_dev_s *dev,
+ FAR struct fb_setcursor_s *settings);
+
+ - Get the LCD panel power status (0: full off - CONFIG_LCD_MAXPOWER:
+ full on). On backlit LCDs, this setting may correspond to the
+ backlight setting.
+
+ int (*getpower)(struct lcd_dev_s *dev);
+
+ - Enable/disable LCD panel power (0: full off - CONFIG_LCD_MAXPOWER:
+ full on). On backlit LCDs, this setting may correspond to the
+ backlight setting.
+
+ int (*setpower)(struct lcd_dev_s *dev, int power);
+
+ - Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST) */
+
+ int (*getcontrast)(struct lcd_dev_s *dev);
+
+ - Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST)
+
+ int (*setcontrast)(struct lcd_dev_s *dev, unsigned int contrast);
+
+Binding LCD Drivers
+===================
+
+ LCD drivers are not normally directly accessed by user code, but are
+ usually bound to another, higher level device driver. In general, the
+ binding sequence is:
+
+ 1. Get an instance of struct lcd_dev_s from the hardware-specific LCD
+ device driver, and
+ 2. Provide that instance to the initialization method of the higher
+ level device driver.
+
+Examples: /drivers/lcd/
+=======================
+
+Re-usable LCD drivers reside in the drivers/lcd directory:
+
+ mio283qt2.c. This is a driver for the MI0283QT-2 LCD from Multi-Inno
+ Technology Co., Ltd. This LCD is based on the Himax HX8347-D LCD
+ controller.
+
+ nokia6100.c. Supports the Nokia 6100 display with either the Philips
+ PCF883 or the Epson S1D15G10 display controller. This LCD is used
+ with the Olimex LPC1766-STK (but has not been fully integrated).
+
+ p14201.c. Driver for RiT P14201 series display with SD1329 IC
+ controller. This OLED is used with older versions of the
+ TI/Luminary LM3S8962 Evaluation Kit.
+
+ ssd12989.c. Generic LCD driver for LCDs based on the Solomon Systech
+ SSD1289 LCD controller. Think of this as a template for an LCD driver
+ that you will proably ahve to customize for any particular LCD
+ hardware. (see also configs/hymini-stm32v/src/ssd1289.c below).
+
+ ug-9664hswag01.c. OLED Display Module, UG-9664HSWAG01", Univision
+ Technology Inc. Used with the LPC Xpresso and Embedded Artists
+ base board.
+
+Examples: configs/
+==================
+
+There are additional LCD drivers in the configs/<board>/src directory
+that support additional LCDs. LCD drivers in the configuration directory
+if they support some differ LCD interface (such as a parallel interface)
+that makes then less re-usable:
+
+ configs/compal_e99/src/ssd1783.c
+
+ SSD1783
+
+ configs/hymini-stm32v/src/ssd1289.c. See also drivers/lcd/ssd1298.c
+ above.
+
+ SSD1289
+
+ configs/kwikstik-k40/src/up_lcd.c. Don't waste your time. This is
+ just a stub.
+
+ configs/olimex-lpc1766stk/src/up_lcd.c. This examples is the
+ bottom half for the SSD1289 driver at drivers/lcd/nokia6100.c.
+ This was never completedly debugged ... there are probably issues
+ with that nasty 9-bit SPI interfaces.
+
+ configs/sam3u-ek/src/up_lcd.c
+
+ The SAM3U-EK developement board features a TFT/Transmissive color
+ LCD module with touch-screen, FTM280C12D, with integrated driver IC
+ HX8346.
+
+ configs/skp16c26/src/up_lcd.c. Untested alphanumeric LCD driver.
+
+ configs/stm3210e-eval/src/up_lcd.c
+
+ This driver supports the following LCDs:
+
+ 1. Ampire AM-240320LTNQW00H
+ 2. Orise Tech SPFD5408B
+ 3. RenesasSP R61580
+
+ configs/stm3220g-eval/src/up_lcd.c and configs/stm3240g-eval/src/up_lcd.c.
+ AM-240320L8TNQW00H (LCD_ILI9320 or LCD_ILI9321) and
+ AM-240320D5TOQW01H (LCD_ILI9325)
+
+ configs/stm32f4discovery/src/up_ssd1289.c. This examples is the
+ bottom half for the SSD1289 driver at drivers/lcd/ssd1289.c
+
+graphics/
+=========
+
+ See also the usage of the LCD driver in the graphics/ directory.
diff --git a/nuttx/drivers/lcd/mio283qt2.c b/nuttx/drivers/lcd/mio283qt2.c
new file mode 100644
index 000000000..1758e230c
--- /dev/null
+++ b/nuttx/drivers/lcd/mio283qt2.c
@@ -0,0 +1,1014 @@
+/**************************************************************************************
+ * drivers/lcd/mio283qt2.c
+ *
+ * This is a driver for the MI0283QT-2 LCD from Multi-Inno Technology Co., Ltd. This
+ * LCD is based on the Himax HX8347-D LCD controller.
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Authors: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * 1) LCD Module Specification, Model : MI0283QT-2, Multi-Inno Technology Co.,
+ * Ltd., Revision 1.0
+ * 2) Data Sheet: HX8347-D(T), 240RGB x 320 dot, 262K color, with internal GRAM, TFT
+ * Mobile Single Chip Driver Version 02 March, Doc No. HX8347-D(T)-DS, Himax
+ * Technologies, Inc., 2009,
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Included Files
+ **************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/mio283qt2.h>
+
+#ifdef CONFIG_LCD_MIO283QT2
+
+/**************************************************************************************
+ * Pre-processor Definitions
+ **************************************************************************************/
+/* Configuration **********************************************************************/
+
+/* Check contrast selection */
+
+#if !defined(CONFIG_LCD_MAXCONTRAST)
+# define CONFIG_LCD_MAXCONTRAST 1
+#endif
+
+/* Check power setting */
+
+#if !defined(CONFIG_LCD_MAXPOWER) || CONFIG_LCD_MAXPOWER < 1
+# define CONFIG_LCD_MAXPOWER 1
+#endif
+
+#if CONFIG_LCD_MAXPOWER > 255
+# error "CONFIG_LCD_MAXPOWER must be less than 256 to fit in uint8_t"
+#endif
+
+/* Check orientation */
+
+#if defined(CONFIG_LCD_PORTRAIT)
+# if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE) || defined(CONFIG_LCD_RPORTRAIT)
+# error "Cannot define both portrait and any other orientations"
+# endif
+#elif defined(CONFIG_LCD_RPORTRAIT)
+# if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
+# error "Cannot define both rportrait and any other orientations"
+# endif
+#elif defined(CONFIG_LCD_LANDSCAPE)
+# ifdef CONFIG_LCD_RLANDSCAPE
+# error "Cannot define both landscape and any other orientations"
+# endif
+#elif !defined(CONFIG_LCD_RLANDSCAPE)
+# define CONFIG_LCD_LANDSCAPE 1
+#endif
+
+/* Define CONFIG_DEBUG_LCD to enable detailed LCD debug output. Verbose debug must
+ * also be enabled.
+ */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_DEBUG_GRAPHICS
+# undef CONFIG_DEBUG_LCD
+#endif
+
+#ifndef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_DEBUG_LCD
+#endif
+
+/* Display/Color Properties ***********************************************************/
+/* Display Resolution */
+
+#if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
+# define MIO283QT2_XRES 320
+# define MIO283QT2_YRES 240
+#else
+# define MIO283QT2_XRES 240
+# define MIO283QT2_YRES 320
+#endif
+
+/* Color depth and format */
+
+#define MIO283QT2_BPP 16
+#define MIO283QT2_COLORFMT FB_FMT_RGB16_565
+
+/* Hardware LCD/LCD controller definitions ********************************************/
+/* In this driver, I chose to use all literal constants for register address and
+ * values. Some recent experiences have shown me that during LCD bringup, it is more
+ * important to know the binary values rather than nice, people friendly names. Sad,
+ * but true.
+ */
+
+#define HIMAX_ID 0x0047
+
+/* LCD Profiles ***********************************************************************/
+/* Many details of the controller initialization must, unfortunately, vary from LCD to
+ * LCD. I have looked at the spec and at three different drivers for LCDs that have
+ * MIO283QT2 controllers. I have tried to summarize these differences as "LCD profiles"
+ *
+ * Most of the differences between LCDs are nothing more than a few minor bit
+ * settings. The most significant difference betwen LCD drivers in is the
+ * manner in which the LCD is powered up and in how the power controls are set.
+ * My suggestion is that if you have working LCD initialization code, you should
+ * simply replace the code in mio283qt2_hwinitialize with your working code.
+ */
+
+#if defined (CONFIG_MIO283QT2_PROFILE2)
+# undef MIO283QT2_USE_SIMPLE_INIT
+
+ /* PWRCTRL1: AP=smalll-to-medium, DC=Flinex24, BT=+5/-4, DCT=Flinex24 */
+
+# define PWRCTRL1_SETTING \
+ (MIO283QT2_PWRCTRL1_AP_SMMED | MIO283QT2_PWRCTRL1_DC_FLINEx24 | \
+ MIO283QT2_PWRCTRL1_BT_p5m4 | MIO283QT2_PWRCTRL1_DCT_FLINEx24)
+
+ /* PWRCTRL2: 5.1v */
+
+# define PWRCTRL2_SETTING MIO283QT2_PWRCTRL2_VRC_5p1V
+
+ /* PWRCTRL3: x 2.165
+ * NOTE: Many drivers have bit 8 set which is not defined in the MIO283QT2 spec.
+ */
+
+# define PWRCTRL3_SETTING MIO283QT2_PWRCTRL3_VRH_x2p165
+
+ /* PWRCTRL4: VDV=9 + VCOMG */
+
+# define PWRCTRL4_SETTING (MIO283QT2_PWRCTRL4_VDV(9) | MIO283QT2_PWRCTRL4_VCOMG)
+
+ /* PWRCTRL5: VCM=56 + NOTP */
+
+# define PWRCTRL5_SETTING (MIO283QT2_PWRCTRL5_VCM(56) | MIO283QT2_PWRCTRL5_NOTP)
+
+#elif defined (CONFIG_MIO283QT2_PROFILE3)
+# undef MIO283QT2_USE_SIMPLE_INIT
+
+ /* PWRCTRL1: AP=smalll-to-medium, DC=Flinex24, BT=+5/-4, DCT=Flinex24 */
+
+# define PWRCTRL1_SETTING \
+ (MIO283QT2_PWRCTRL1_AP_SMMED | MIO283QT2_PWRCTRL1_DC_FLINEx24 | \
+ MIO283QT2_PWRCTRL1_BT_p5m4 | MIO283QT2_PWRCTRL1_DCT_FLINEx24)
+
+ /* PWRCTRL2: 5.1v */
+
+# define PWRCTRL2_SETTING MIO283QT2_PWRCTRL2_VRC_5p1V
+
+ /* PWRCTRL3: x 2.165
+ * NOTE: Many drivers have bit 8 set which is not defined in the MIO283QT2 spec.
+ */
+
+# define PWRCTRL3_SETTING MIO283QT2_PWRCTRL3_VRH_x2p165
+
+ /* PWRCTRL4: VDV=9 + VCOMG */
+
+# define PWRCTRL4_SETTING (MIO283QT2_PWRCTRL4_VDV(9) | MIO283QT2_PWRCTRL4_VCOMG)
+
+ /* PWRCTRL5: VCM=56 + NOTP */
+
+# define PWRCTRL5_SETTING (MIO283QT2_PWRCTRL5_VCM(56) | MIO283QT2_PWRCTRL5_NOTP)
+
+#else /* if defined (CONFIG_MIO283QT2_PROFILE1) */
+# undef MIO283QT2_USE_SIMPLE_INIT
+# define MIO283QT2_USE_SIMPLE_INIT 1
+
+ /* PWRCTRL1: AP=medium-to-large, DC=Fosc/4, BT=+5/-4, DCT=Fosc/4 */
+
+# define PWRCTRL1_SETTING \
+ (MIO283QT2_PWRCTRL1_AP_MEDLG | MIO283QT2_PWRCTRL1_DC_FOSd4 | \
+ MIO283QT2_PWRCTRL1_BT_p5m4 | MIO283QT2_PWRCTRL1_DCT_FOSd4)
+
+ /* PWRCTRL2: 5.3v */
+
+# define PWRCTRL2_SETTING MIO283QT2_PWRCTRL2_VRC_5p3V
+
+ /* PWRCTRL3: x 2.570
+ * NOTE: Many drivers have bit 8 set which is not defined in the MIO283QT2 spec.
+ */
+
+# define PWRCTRL3_SETTING MIO283QT2_PWRCTRL3_VRH_x2p570
+
+ /* PWRCTRL4: VDV=12 + VCOMG */
+
+# define PWRCTRL4_SETTING (MIO283QT2_PWRCTRL4_VDV(12) | MIO283QT2_PWRCTRL4_VCOMG)
+
+ /* PWRCTRL5: VCM=60 + NOTP */
+
+# define PWRCTRL5_SETTING (MIO283QT2_PWRCTRL5_VCM(60) | MIO283QT2_PWRCTRL5_NOTP)
+
+#endif
+
+/* Debug ******************************************************************************/
+
+#ifdef CONFIG_DEBUG_LCD
+# define lcddbg dbg
+# define lcdvdbg vdbg
+#else
+# define lcddbg(x...)
+# define lcdvdbg(x...)
+#endif
+
+/**************************************************************************************
+ * Private Type Definition
+ **************************************************************************************/
+
+/* This structure describes the state of this driver */
+
+struct mio283qt2_dev_s
+{
+ /* Publically visible device structure */
+
+ struct lcd_dev_s dev;
+
+ /* Private LCD-specific information follows */
+
+ FAR struct mio283qt2_lcd_s *lcd; /* The contained platform-specific, LCD interface */
+ uint8_t power; /* Current power setting */
+
+ /* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane. This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8). Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently. However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+ uint16_t runbuffer[MIO283QT2_XRES];
+};
+
+/**************************************************************************************
+ * Private Function Protototypes
+ **************************************************************************************/
+/* Low Level LCD access */
+
+static void mio283qt2_putreg(FAR struct mio283qt2_lcd_s *lcd, uint8_t regaddr,
+ uint16_t regval);
+#ifndef CONFIG_LCD_NOGETRUN
+static uint16_t mio283qt2_readreg(FAR struct mio283qt2_lcd_s *lcd, uint8_t regaddr);
+#endif
+static inline void mio283qt2_gramwrite(FAR struct mio283qt2_lcd_s *lcd,
+ uint16_t rgbcolor);
+#ifndef CONFIG_LCD_NOGETRUN
+static inline void mio283qt2_readsetup(FAR struct mio283qt2_lcd_s *lcd,
+ FAR uint16_t *accum);
+static inline uint16_t mio283qt2_gramread(FAR struct mio283qt2_lcd_s *lcd,
+ FAR uint16_t *accum);
+#endif
+static void mio283qt2_setarea(FAR struct mio283qt2_lcd_s *lcd,
+ uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
+
+/* LCD Data Transfer Methods */
+
+static int mio283qt2_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels);
+static int mio283qt2_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels);
+
+/* LCD Configuration */
+
+static int mio283qt2_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo);
+static int mio283qt2_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+# error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+# error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int mio283qt2_getpower(FAR struct lcd_dev_s *dev);
+static int mio283qt2_setpower(FAR struct lcd_dev_s *dev, int power);
+static int mio283qt2_getcontrast(FAR struct lcd_dev_s *dev);
+static int mio283qt2_setcontrast(FAR struct lcd_dev_s *dev, unsigned int contrast);
+
+/* Initialization */
+
+static inline int mio283qt2_hwinitialize(FAR struct mio283qt2_dev_s *priv);
+
+/**************************************************************************************
+ * Private Data
+ **************************************************************************************/
+
+/* This driver can support only a signal MIO283QT2 device. This is due to an
+ * unfortunate decision made whent he getrun and putrun methods were designed. The
+ * following is the single MIO283QT2 driver state instance:
+ */
+
+static struct mio283qt2_dev_s g_lcddev;
+
+/**************************************************************************************
+ * Private Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: mio283qt2_putreg(lcd,
+ *
+ * Description:
+ * Write to an LCD register
+ *
+ **************************************************************************************/
+
+static void mio283qt2_putreg(FAR struct mio283qt2_lcd_s *lcd,
+ uint8_t regaddr, uint16_t regval)
+{
+ /* Set the index register to the register address and write the register contents */
+
+ lcd->index(lcd, regaddr);
+ lcd->write(lcd, regval);
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_readreg
+ *
+ * Description:
+ * Read from an LCD register
+ *
+ **************************************************************************************/
+
+#ifndef CONFIG_LCD_NOGETRUN
+static uint16_t mio283qt2_readreg(FAR struct mio283qt2_lcd_s *lcd, uint8_t regaddr)
+{
+ /* Set the index register to the register address and read the register contents. */
+
+ lcd->index(lcd, regaddr);
+ return lcd->read(lcd);
+}
+#endif
+
+/**************************************************************************************
+ * Name: mio283qt2_gramselect
+ *
+ * Description:
+ * Setup to read or write multiple pixels to the GRAM memory
+ *
+ **************************************************************************************/
+
+static inline void mio283qt2_gramselect(FAR struct mio283qt2_lcd_s *lcd)
+{
+ lcd->index(lcd, 0x22);
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_gramwrite
+ *
+ * Description:
+ * Setup to read or write multiple pixels to the GRAM memory
+ *
+ **************************************************************************************/
+
+static inline void mio283qt2_gramwrite(FAR struct mio283qt2_lcd_s *lcd, uint16_t data)
+{
+ lcd->write(lcd, data);
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_readsetup
+ *
+ * Description:
+ * Prime the operation by reading one pixel from the GRAM memory if necessary for
+ * this LCD type. When reading 16-bit gram data, there may be some shifts in the
+ * returned data:
+ *
+ * - ILI932x: Discard first dummy read; no shift in the return data
+ *
+ **************************************************************************************/
+
+#ifndef CONFIG_LCD_NOGETRUN
+static inline void mio283qt2_readsetup(FAR struct mio283qt2_lcd_s *lcd,
+ FAR uint16_t *accum)
+{
+#if 0 /* Probably not necessary... untested */
+ /* Read-ahead one pixel */
+
+ *accum = lcd->read(lcd);
+#endif
+}
+#endif
+
+/**************************************************************************************
+ * Name: mio283qt2_gramread
+ *
+ * Description:
+ * Read one correctly aligned pixel from the GRAM memory. Possibly shifting the
+ * data and possibly swapping red and green components.
+ *
+ * - ILI932x: Unknown -- assuming colors are in the color order
+ *
+ **************************************************************************************/
+
+#ifndef CONFIG_LCD_NOGETRUN
+static inline uint16_t mio283qt2_gramread(FAR struct mio283qt2_lcd_s *lcd,
+ FAR uint16_t *accum)
+{
+ /* Read the value (GRAM register already selected) */
+
+ return lcd->read(lcd);
+}
+#endif
+
+/**************************************************************************************
+ * Name: mio283qt2_setarea
+ *
+ * Description:
+ * Set the cursor position. In landscape mode, the "column" is actually the physical
+ * Y position and the "row" is the physical X position.
+ *
+ **************************************************************************************/
+
+static void mio283qt2_setarea(FAR struct mio283qt2_lcd_s *lcd,
+ uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
+{
+ mio283qt2_putreg(lcd, 0x03, (x0 & 0x00ff)); /* set x0 */
+ mio283qt2_putreg(lcd, 0x02, (x0 >> 8)); /* set x0 */
+ mio283qt2_putreg(lcd, 0x05, (x1 & 0x00ff)); /* set x1 */
+ mio283qt2_putreg(lcd, 0x04, (x1 >> 8)); /* set x1 */
+ mio283qt2_putreg(lcd, 0x07, (y0 & 0x00ff)); /* set y0 */
+ mio283qt2_putreg(lcd, 0x06, (y0 >> 8)); /* set y0 */
+ mio283qt2_putreg(lcd, 0x09, (y1 & 0x00ff)); /* set y1 */
+ mio283qt2_putreg(lcd, 0x08, (y1 >> 8)); /* set y1 */
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_dumprun
+ *
+ * Description:
+ * Dump the contexts of the run buffer:
+ *
+ * run - The buffer in containing the run read to be dumped
+ * npixels - The number of pixels to dump
+ *
+ **************************************************************************************/
+
+#if 0 /* Sometimes useful */
+static void mio283qt2_dumprun(FAR const char *msg, FAR uint16_t *run, size_t npixels)
+{
+ int i, j;
+
+ lib_rawprintf("\n%s:\n", msg);
+ for (i = 0; i < npixels; i += 16)
+ {
+ up_putc(' ');
+ lib_rawprintf(" ");
+ for (j = 0; j < 16; j++)
+ {
+ lib_rawprintf(" %04x", *run++);
+ }
+ up_putc('\n');
+ }
+}
+#endif
+
+/**************************************************************************************
+ * Name: mio283qt2_putrun
+ *
+ * Description:
+ * This method can be used to write a partial raster line to the LCD:
+ *
+ * row - Starting row to write to (range: 0 <= row < yres)
+ * col - Starting column to write to (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer containing the run to be written to the LCD
+ * npixels - The number of pixels to write to the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+static int mio283qt2_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels)
+{
+ FAR struct mio283qt2_dev_s *priv = &g_lcddev;
+ FAR struct mio283qt2_lcd_s *lcd = priv->lcd;
+ FAR const uint16_t *src = (FAR const uint16_t*)buffer;
+ int i;
+
+ /* Buffer must be provided and aligned to a 16-bit address boundary */
+
+ lcdvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);
+
+ /* Select the LCD */
+
+ lcd->select(lcd);
+
+ /* Write the run to GRAM. */
+
+ mio283qt2_setarea(lcd, col, row, col + npixels - 1, row);
+ mio283qt2_gramselect(lcd);
+
+ for (i = 0; i < npixels; i++)
+ {
+ mio283qt2_gramwrite(lcd, *src);
+ src++;
+ }
+
+ /* De-select the LCD */
+
+ lcd->deselect(lcd);
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_getrun
+ *
+ * Description:
+ * This method can be used to read a partial raster line from the LCD:
+ *
+ * row - Starting row to read from (range: 0 <= row < yres)
+ * col - Starting column to read read (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer in which to return the run read from the LCD
+ * npixels - The number of pixels to read from the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+static int mio283qt2_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels)
+{
+#ifndef CONFIG_LCD_NOGETRUN
+ FAR struct mio283qt2_dev_s *priv = &g_lcddev;
+ FAR struct mio283qt2_lcd_s *lcd = priv->lcd;
+ FAR uint16_t *dest = (FAR uint16_t*)buffer;
+ uint16_t accum;
+ int i;
+
+ /* Buffer must be provided and aligned to a 16-bit address boundary */
+
+ lcdvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);
+
+ /* Select the LCD */
+
+ lcd->select(lcd);
+
+ /* Read the run from GRAM. */
+
+ /* Select the LCD */
+
+ lcd->select(lcd);
+
+ /* Red the run fram GRAM. */
+
+ mio283qt2_setarea(lcd, col, row, col + npixels - 1, row);
+ mio283qt2_gramselect(lcd);
+
+ /* Prime the pump for unaligned read data */
+
+ mio283qt2_readsetup(lcd, &accum);
+
+ for (i = 0; i < npixels; i++)
+ {
+ *dest++ = mio283qt2_gramread(lcd, &accum);
+ }
+
+ /* De-select the LCD */
+
+ lcd->deselect(lcd);
+ return OK;
+#else
+ return -ENOSYS;
+#endif
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_getvideoinfo
+ *
+ * Description:
+ * Get information about the LCD video controller configuration.
+ *
+ **************************************************************************************/
+
+static int mio283qt2_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo)
+{
+ DEBUGASSERT(dev && vinfo);
+ lcdvdbg("fmt: %d xres: %d yres: %d nplanes: 1\n",
+ MIO283QT2_COLORFMT, MIO283QT2_XRES, MIO283QT2_XRES);
+
+ vinfo->fmt = MIO283QT2_COLORFMT; /* Color format: RGB16-565: RRRR RGGG GGGB BBBB */
+ vinfo->xres = MIO283QT2_XRES; /* Horizontal resolution in pixel columns */
+ vinfo->yres = MIO283QT2_YRES; /* Vertical resolution in pixel rows */
+ vinfo->nplanes = 1; /* Number of color planes supported */
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_getplaneinfo
+ *
+ * Description:
+ * Get information about the configuration of each LCD color plane.
+ *
+ **************************************************************************************/
+
+static int mio283qt2_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo)
+{
+ FAR struct mio283qt2_dev_s *priv = (FAR struct mio283qt2_dev_s *)dev;
+
+ DEBUGASSERT(dev && pinfo && planeno == 0);
+ lcdvdbg("planeno: %d bpp: %d\n", planeno, MIO283QT2_BPP);
+
+ pinfo->putrun = mio283qt2_putrun; /* Put a run into LCD memory */
+ pinfo->getrun = mio283qt2_getrun; /* Get a run from LCD memory */
+ pinfo->buffer = (uint8_t*)priv->runbuffer; /* Run scratch buffer */
+ pinfo->bpp = MIO283QT2_BPP; /* Bits-per-pixel */
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_getpower
+ *
+ * Description:
+ * Get the LCD panel power status (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int mio283qt2_getpower(FAR struct lcd_dev_s *dev)
+{
+ lcdvdbg("power: %d\n", 0);
+ return g_lcddev.power;
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_poweroff
+ *
+ * Description:
+ * Enable/disable LCD panel power (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int mio283qt2_poweroff(FAR struct mio283qt2_lcd_s *lcd)
+{
+ /* Set the backlight off */
+
+ lcd->backlight(lcd, 0);
+
+ /* Turn the display off */
+
+ mio283qt2_putreg(lcd, 0x28, 0x0000); /* GON=0, DTE=0, D=0 */
+
+ /* Remember the power off state */
+
+ g_lcddev.power = 0;
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_setpower
+ *
+ * Description:
+ * Enable/disable LCD panel power (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int mio283qt2_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+ FAR struct mio283qt2_dev_s *priv = (FAR struct mio283qt2_dev_s *)dev;
+ FAR struct mio283qt2_lcd_s *lcd = priv->lcd;
+
+ lcdvdbg("power: %d\n", power);
+ DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER);
+
+ /* Set new power level */
+
+ if (power > 0)
+ {
+ /* Set the backlight level */
+
+ lcd->backlight(lcd, power);
+
+ /* Then turn the display on:
+ * D=ON(3) CM=0 DTE=1 GON=1 SPT=0 VLE=0 PT=0
+ */
+
+ /* Display on */
+
+ mio283qt2_putreg(lcd, 0x28, 0x0038); /* GON=1, DTE=1, D=2 */
+ up_mdelay(40);
+ mio283qt2_putreg(lcd, 0x28, 0x003c); /* GON=1, DTE=1, D=3 */
+
+ g_lcddev.power = power;
+ }
+ else
+ {
+ /* Turn the display off */
+
+ mio283qt2_poweroff(lcd);
+ }
+
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_getcontrast
+ *
+ * Description:
+ * Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int mio283qt2_getcontrast(FAR struct lcd_dev_s *dev)
+{
+ lcdvdbg("Not implemented\n");
+ return -ENOSYS;
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_setcontrast
+ *
+ * Description:
+ * Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int mio283qt2_setcontrast(FAR struct lcd_dev_s *dev, unsigned int contrast)
+{
+ lcdvdbg("contrast: %d\n", contrast);
+ return -ENOSYS;
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_hwinitialize
+ *
+ * Description:
+ * Initialize the LCD hardware.
+ *
+ **************************************************************************************/
+
+static inline int mio283qt2_hwinitialize(FAR struct mio283qt2_dev_s *priv)
+{
+ FAR struct mio283qt2_lcd_s *lcd = priv->lcd;
+#ifndef CONFIG_LCD_NOGETRUN
+ uint16_t id;
+#endif
+
+ /* Select the LCD */
+
+ lcd->select(lcd);
+
+ /* Read the HIMAX ID registger (0x00) */
+
+#ifndef CONFIG_LCD_NOGETRUN
+ id = mio283qt2_readreg(lcd, 0x00);
+ lcddbg("LCD ID: %04x\n", id);
+
+ /* Check if the ID is for the MIO283QT2 */
+
+ if (id == HIMAX_ID)
+#endif
+ {
+ /* Driving ability */
+
+ mio283qt2_putreg(lcd, 0xea, 0x0000); /* PTBA[15:8] */
+ mio283qt2_putreg(lcd, 0xeb, 0x0020); /* PTBA[7:0] */
+ mio283qt2_putreg(lcd, 0xec, 0x000c); /* STBA[15:8] */
+ mio283qt2_putreg(lcd, 0xed, 0x00c4); /* STBA[7:0] */
+ mio283qt2_putreg(lcd, 0xe8, 0x0040); /* OPON[7:0] */
+ mio283qt2_putreg(lcd, 0xe9, 0x0038); /* OPON1[7:0] */
+ mio283qt2_putreg(lcd, 0xf1, 0x0001); /* OTPS1B */
+ mio283qt2_putreg(lcd, 0xf2, 0x0010); /* GEN */
+ mio283qt2_putreg(lcd, 0x27, 0x00a3);
+
+ /* Power voltage */
+
+ mio283qt2_putreg(lcd, 0x1b, 0x001b); /* VRH = 4.65 */
+ mio283qt2_putreg(lcd, 0x1a, 0x0001); /* BT */
+ mio283qt2_putreg(lcd, 0x24, 0x002f); /* VMH */
+ mio283qt2_putreg(lcd, 0x25, 0x0057); /* VML */
+
+ /* Vcom offset */
+
+ mio283qt2_putreg(lcd, 0x23, 0x008d); /* For flicker adjust */
+
+ /* Power on */
+
+ mio283qt2_putreg(lcd, 0x18, 0x0036);
+ mio283qt2_putreg(lcd, 0x19, 0x0001); /* Start oscillator */
+ mio283qt2_putreg(lcd, 0x01, 0x0000); /* Wakeup */
+ mio283qt2_putreg(lcd, 0x1f, 0x0088);
+ up_mdelay(5);
+ mio283qt2_putreg(lcd, 0x1f, 0x0080);
+ up_mdelay(5);
+ mio283qt2_putreg(lcd, 0x1f, 0x0090);
+ up_mdelay(5);
+ mio283qt2_putreg(lcd, 0x1f, 0x00d0);
+ up_mdelay(5);
+
+ /* Gamma 2.8 setting */
+
+ mio283qt2_putreg(lcd, 0x40, 0x0000);
+ mio283qt2_putreg(lcd, 0x41, 0x0000);
+ mio283qt2_putreg(lcd, 0x42, 0x0001);
+ mio283qt2_putreg(lcd, 0x43, 0x0013);
+ mio283qt2_putreg(lcd, 0x44, 0x0010);
+ mio283qt2_putreg(lcd, 0x45, 0x0026);
+ mio283qt2_putreg(lcd, 0x46, 0x0008);
+ mio283qt2_putreg(lcd, 0x47, 0x0051);
+ mio283qt2_putreg(lcd, 0x48, 0x0002);
+ mio283qt2_putreg(lcd, 0x49, 0x0012);
+ mio283qt2_putreg(lcd, 0x4a, 0x0018);
+ mio283qt2_putreg(lcd, 0x4b, 0x0019);
+ mio283qt2_putreg(lcd, 0x4c, 0x0014);
+
+ mio283qt2_putreg(lcd, 0x50, 0x0019);
+ mio283qt2_putreg(lcd, 0x51, 0x002f);
+ mio283qt2_putreg(lcd, 0x52, 0x002c);
+ mio283qt2_putreg(lcd, 0x53, 0x003e);
+ mio283qt2_putreg(lcd, 0x54, 0x003f);
+ mio283qt2_putreg(lcd, 0x55, 0x003f);
+ mio283qt2_putreg(lcd, 0x56, 0x002e);
+ mio283qt2_putreg(lcd, 0x57, 0x0077);
+ mio283qt2_putreg(lcd, 0x58, 0x000b);
+ mio283qt2_putreg(lcd, 0x59, 0x0006);
+ mio283qt2_putreg(lcd, 0x5a, 0x0007);
+ mio283qt2_putreg(lcd, 0x5b, 0x000d);
+ mio283qt2_putreg(lcd, 0x5c, 0x001d);
+ mio283qt2_putreg(lcd, 0x5d, 0x00cc);
+
+ /* 4K Color Selection */
+
+ mio283qt2_putreg(lcd, 0x17, 0x0003);
+ mio283qt2_putreg(lcd, 0x17, 0x0005); /* 0x0005=65k, 0x0006=262k */
+
+ /* Panel characteristics */
+
+ mio283qt2_putreg(lcd, 0x36, 0x0000);
+
+ /* Display Setting */
+
+ mio283qt2_putreg(lcd, 0x01, 0x0000); /* IDMON=0, INVON=0, NORON=0, PTLON=0 */
+
+#if defined(CONFIG_LCD_LANDSCAPE)
+ mio283qt2_putreg(lcd, 0x16, 0x00a8); /* MY=1, MX=0, MV=1, ML=0, BGR=1 */
+#elif defined(CONFIG_LCD_PORTRAIT)
+ mio283qt2_putreg(lcd, 0x16, 0x0008); /* MY=0, MX=0, MV=0, ML=0, BGR=1 */
+#elif defined(CONFIG_LCD_RLANDSCAPE)
+ mio283qt2_putreg(lcd, 0x16, 0x0068); /* MY=0, MX=1, MV=1, ML=0, BGR=1 */
+#elif defined(CONFIG_LCD_RPORTRAIT)
+ mio283qt2_putreg(lcd, 0x16, 0x00c8); /* MY=1, MX=0, MV=1, ML=0, BGR=1 */
+#endif
+
+ /* Window setting */
+
+ mio283qt2_setarea(lcd, 0, 0, (MIO283QT2_XRES-1), (MIO283QT2_YRES-1));
+ return OK;
+ }
+#ifndef CONFIG_LCD_NOGETRUN
+ else
+ {
+ lcddbg("Unsupported LCD type\n");
+ return -ENODEV;
+ }
+#endif
+
+ /* De-select the LCD */
+
+ lcd->deselect(lcd);
+}
+
+ /*************************************************************************************
+ * Public Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: mio283qt2_lcdinitialize
+ *
+ * Description:
+ * Initialize the LCD video hardware. The initial state of the LCD is fully
+ * initialized, display memory cleared, and the LCD ready to use, but with the power
+ * setting at 0 (full off).
+ *
+ **************************************************************************************/
+
+FAR struct lcd_dev_s *mio283qt2_lcdinitialize(FAR struct mio283qt2_lcd_s *lcd)
+{
+ int ret;
+
+ lcdvdbg("Initializing\n");
+
+ /* If we ccould support multiple MIO283QT2 devices, this is where we would allocate
+ * a new driver data structure... but we can't. Why not? Because of a bad should
+ * the form of the getrun() and putrun methods.
+ */
+
+ FAR struct mio283qt2_dev_s *priv = &g_lcddev;
+
+ /* Initialize the driver data structure */
+
+ priv->dev.getvideoinfo = mio283qt2_getvideoinfo;
+ priv->dev.getplaneinfo = mio283qt2_getplaneinfo;
+ priv->dev.getpower = mio283qt2_getpower;
+ priv->dev.setpower = mio283qt2_setpower;
+ priv->dev.getcontrast = mio283qt2_getcontrast;
+ priv->dev.setcontrast = mio283qt2_setcontrast;
+ priv->lcd = lcd;
+
+ /* Configure and enable LCD */
+
+ ret = mio283qt2_hwinitialize(priv);
+ if (ret == OK)
+ {
+ /* Clear the display (setting it to the color 0=black) */
+
+ mio283qt2_clear(&priv->dev, 0);
+
+ /* Turn the display off */
+
+ mio283qt2_poweroff(lcd);
+ return &g_lcddev.dev;
+ }
+
+ return NULL;
+}
+
+/**************************************************************************************
+ * Name: mio283qt2_clear
+ *
+ * Description:
+ * This is a non-standard LCD interface just for the stm3240g-EVAL board. Because
+ * of the various rotations, clearing the display in the normal way by writing a
+ * sequences of runs that covers the entire display can be very slow. Here the
+ * display is cleared by simply setting all GRAM memory to the specified color.
+ *
+ **************************************************************************************/
+
+void mio283qt2_clear(FAR struct lcd_dev_s *dev, uint16_t color)
+{
+ FAR struct mio283qt2_dev_s *priv = (FAR struct mio283qt2_dev_s *)dev;
+ FAR struct mio283qt2_lcd_s *lcd = priv->lcd;
+ uint32_t i;
+
+ /* Select the LCD and set the drawring area */
+
+ lcd->select(lcd);
+ mio283qt2_setarea(lcd, 0, 0, (MIO283QT2_XRES-1), (MIO283QT2_YRES-1));
+
+ /* Prepare to write GRAM data */
+
+ mio283qt2_gramselect(lcd);
+
+ /* Copy color into all of GRAM. Orientation does not matter in this case. */
+
+ for (i = 0; i < MIO283QT2_XRES * MIO283QT2_YRES; i++)
+ {
+ mio283qt2_gramwrite(lcd, color);
+ }
+
+ /* De-select the LCD */
+
+ lcd->deselect(lcd);
+}
+
+#endif /* CONFIG_LCD_MIO283QT2 */
diff --git a/nuttx/drivers/lcd/nokia6100.c b/nuttx/drivers/lcd/nokia6100.c
new file mode 100644
index 000000000..7354b8a91
--- /dev/null
+++ b/nuttx/drivers/lcd/nokia6100.c
@@ -0,0 +1,1230 @@
+/**************************************************************************************
+ * drivers/lcd/nokia6100.c
+ * Nokia 6100 LCD Display Driver
+ *
+ * Copyright (C) 2010-2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * "Nokia 6100 LCD Display Driver," Revision 1, James P. Lynch ("Nokia 6100 LCD
+ * Display Driver.pdf")
+ * "S1D15G0D08B000," Seiko Epson Corportation, 2002.
+ * "Data Sheet, PCF8833 STN RGB 132x132x3 driver," Phillips, 2003 Feb 14.
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Included Files
+ **************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/nokia6100.h>
+
+#ifdef CONFIG_NOKIA6100_PCF8833
+# include "pcf8833.h"
+#endif
+#ifdef CONFIG_NOKIA6100_S1D15G10
+# include "s1d15g10.h"
+#endif
+
+/**************************************************************************************
+ * Pre-processor Definitions
+ **************************************************************************************/
+
+/* Configuration **********************************************************************/
+/* Verify that all configuration requirements have been met */
+
+/* Nokia 6100 Configuration Settings:
+ *
+ * CONFIG_NOKIA6100_SPIMODE - Controls the SPI mode
+ * CONFIG_NOKIA6100_FREQUENCY - Define to use a different bus frequency
+ * CONFIG_NOKIA6100_NINTERFACES - Specifies the number of physical Nokia 6100 devices that
+ * will be supported.
+ * CONFIG_NOKIA6100_BPP - Device supports 8, 12, and 16 bits per pixel.
+ * CONFIG_NOKIA6100_S1D15G10 - Selects the Epson S1D15G10 display controller
+ * CONFIG_NOKIA6100_PCF8833 - Selects the Phillips PCF8833 display controller
+ * CONFIG_NOKIA6100_BLINIT - Initial backlight setting
+ *
+ * The following may need to be tuned for your hardware:
+ * CONFIG_NOKIA6100_INVERT - Display inversion, 0 or 1, Default: 1
+ * CONFIG_NOKIA6100_MY - Display row direction, 0 or 1, Default: 0
+ * CONFIG_NOKIA6100_MX - Display column direction, 0 or 1, Default: 1
+ * CONFIG_NOKIA6100_V - Display address direction, 0 or 1, Default: 0
+ * CONFIG_NOKIA6100_ML - Display scan direction, 0 or 1, Default: 0
+ * CONFIG_NOKIA6100_RGBORD - Display RGB order, 0 or 1, Default: 0
+ *
+ * Required LCD driver settings:
+ * CONFIG_LCD_NOKIA6100 - Enable Nokia 6100 support
+ * CONFIG_LCD_MAXCONTRAST - must be 63 with the Epson controller and 127 with
+ * the Phillips controller.
+ * CONFIG_LCD_MAXPOWER - Maximum value of backlight setting. The backlight control is
+ * managed outside of the 6100 driver so this value has no meaning to the driver.
+ */
+
+/* Mode 0,0 should be use. However, somtimes you need to tinker with these things. */
+
+#ifndef CONFIG_NOKIA6100_SPIMODE
+# define CONFIG_NOKIA6100_SPIMODE SPIDEV_MODE0
+#endif
+
+/* Default frequency: 1Mhz */
+
+#ifndef CONFIG_NOKIA6100_FREQUENCY
+# define CONFIG_NOKIA6100_FREQUENCY 1000000
+#endif
+
+/* CONFIG_NOKIA6100_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_NOKIA6100_NINTERFACES
+# define CONFIG_NOKIA6100_NINTERFACES 1
+#endif
+
+#if CONFIG_NOKIA6100_NINTERFACES != 1
+# error "This implementation supports only a single LCD device"
+#endif
+
+/* Only support for 8 and 12 BPP currently implemented */
+
+#if !defined(CONFIG_NOKIA6100_BPP)
+# warning "Assuming 8BPP"
+# define CONFIG_NOKIA6100_BPP 8
+#endif
+
+#if CONFIG_NOKIA6100_BPP != 8 && CONFIG_NOKIA6100_BPP != 12
+# if CONFIG_NOKIA6100_BPP == 16
+# error "Support for 16BPP no yet implemented"
+# else
+# error "LCD supports only 8, 12, and 16BPP"
+# endif
+#endif
+
+#if CONFIG_NOKIA6100_BPP == 8
+# ifdef CONFIG_NX_DISABLE_8BPP
+# warning "8-bit pixel support needed"
+# endif
+#elif CONFIG_NOKIA6100_BPP == 12
+# if defined(CONFIG_NX_DISABLE_12BPP) || !defined(CONFIG_NX_PACKEDMSFIRST)
+# warning "12-bit, big-endian pixel support needed"
+# endif
+#elif CONFIG_NOKIA6100_BPP == 16
+# ifdef CONFIG_NX_DISABLE_16BPP
+# warning "16-bit pixel support needed"
+# endif
+#endif
+
+/* Exactly one LCD controller must be selected. "The Olimex boards have both display
+ * controllers possible; if the LCD has a GE-12 sticker on it, it’s a Philips PCF8833.
+ * If it has a GE-8 sticker, it’s an Epson controller."
+ */
+
+#if defined(CONFIG_NOKIA6100_S1D15G10) && defined(CONFIG_NOKIA6100_PCF8833)
+# error "Both CONFIG_NOKIA6100_S1D15G10 and CONFIG_NOKIA6100_PCF8833 are defined"
+#endif
+
+#if !defined(CONFIG_NOKIA6100_S1D15G10) && !defined(CONFIG_NOKIA6100_PCF8833)
+# error "One of CONFIG_NOKIA6100_S1D15G10 or CONFIG_NOKIA6100_PCF8833 must be defined"
+#endif
+
+/* Delay geometry defaults */
+
+#ifndef CONFIG_NOKIA6100_INVERT
+# define CONFIG_NOKIA6100_INVERT 1
+#endif
+
+#ifndef CONFIG_NOKIA6100_MY
+# define CONFIG_NOKIA6100_MY 0
+#endif
+
+#ifndef CONFIG_NOKIA6100_MX
+# define CONFIG_NOKIA6100_MX 1
+#endif
+
+#ifndef CONFIG_NOKIA6100_V
+# define CONFIG_NOKIA6100_V 0
+#endif
+
+#ifndef CONFIG_NOKIA6100_ML
+# define CONFIG_NOKIA6100_ML 0
+#endif
+
+#ifndef CONFIG_NOKIA6100_RGBORD
+# define CONFIG_NOKIA6100_RGBORD 0
+#endif
+
+/* Check contrast selection */
+
+#ifdef CONFIG_NOKIA6100_S1D15G10
+
+# if !defined(CONFIG_LCD_MAXCONTRAST)
+# define CONFIG_LCD_MAXCONTRAST 63
+# endif
+# if CONFIG_LCD_MAXCONTRAST != 63
+# error "CONFIG_LCD_MAXCONTRAST must be 63 with the Epson LCD controller"
+# endif
+# define NOKIA_DEFAULT_CONTRAST 32
+
+#else /* CONFIG_NOKIA6100_PCF8833 */
+
+# if !defined(CONFIG_LCD_MAXCONTRAST)
+# define CONFIG_LCD_MAXCONTRAST 127
+# endif
+# if CONFIG_LCD_MAXCONTRAST != 127
+# error "CONFIG_LCD_MAXCONTRAST must be 127 with the Phillips LCD controller"
+# endif
+# define NOKIA_DEFAULT_CONTRAST 48
+
+#endif
+
+/* Check initial backlight setting */
+
+#ifndef CONFIG_NOKIA6100_BLINIT
+# define CONFIG_NOKIA6100_BLINIT (NOKIA_DEFAULT_CONTRAST/3)
+#endif
+
+/* Word width must be 9 bits */
+
+#if defined(CONFIG_NOKIA6100_WORDWIDTH) && CONFIG_NOKIA6100_WORDWIDTH != 9
+# error "CONFIG_NOKIA6100_WORDWIDTH must be 9"
+#endif
+#ifndef CONFIG_NOKIA6100_WORDWIDTH
+# define CONFIG_NOKIA6100_WORDWIDTH 9
+#endif
+
+/* If bit 9 is set, the byte is data; clear for commands */
+
+#define NOKIA_LCD_DATA (1 << 8)
+
+/* Define CONFIG_LCD_REGDEBUG to enable register-level debug output.
+ * (Verbose debug must also be enabled)
+ */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_DEBUG_GRAPHICS
+#endif
+
+#ifndef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_LCD_REGDEBUG
+#endif
+
+/* Controller independent definitions *************************************************/
+
+#ifdef CONFIG_NOKIA6100_PCF8833
+# define LCD_NOP PCF8833_NOP
+# define LCD_RAMWR PCF8833_RAMWR
+# define LCD_PASET PCF8833_PASET
+# define LCD_CASET PCF8833_CASET
+#endif
+#ifdef CONFIG_NOKIA6100_S1D15G10
+# define LCD_NOP S1D15G10_NOP
+# define LCD_RAMWR S1D15G10_RAMWR
+# define LCD_PASET S1D15G10_PASET
+# define LCD_CASET S1D15G10_CASET
+#endif
+
+/* Color Properties *******************************************************************/
+
+/* Display Resolution */
+
+#define NOKIA_XRES 132
+#define NOKIA_YRES 132
+
+/* Color depth and format */
+
+#if CONFIG_NOKIA6100_BPP == 8
+# define NOKIA_BPP 8
+# define NOKIA_COLORFMT FB_FMT_RGB8_332
+# define NOKIA_STRIDE NOKIA_XRES
+# define NOKIA_PIX2BYTES(p) (p)
+#elif CONFIG_NOKIA6100_BPP == 12
+# define NOKIA_BPP 12
+# define NOKIA_COLORFMT FB_FMT_RGB12_444
+# define NOKIA_STRIDE ((3*NOKIA_XRES+1)/2)
+# define NOKIA_PIX2BYTES(p) ((3*(p)+1)/2)
+#elif CONFIG_NOKIA6100_BPP == 16
+# define NOKIA_BPP 16
+# define NOKIA_COLORFMT FB_FMT_RGB16_565
+# define NOKIA_STRIDE (2*NOKIA_XRES)
+# define NOKIA_PIX2BYTES(p) (2*(p))
+#endif
+
+/* Handle any potential strange behavior at edges */
+
+#if 0 /* REVISIT */
+#define NOKIA_PGBIAS 2 /* May not be necessary */
+#define NOKIA_COLBIAS 0
+#define NOKIA_XBIAS 2 /* May not be necessary, if so need to subtract from XRES */
+#define NOKIA_YBIAS 0
+#else
+#define NOKIA_PGBIAS 0
+#define NOKIA_COLBIAS 0
+#define NOKIA_XBIAS 0
+#define NOKIA_YBIAS 0
+#endif
+
+#define NOKIA_ENDPAGE 131
+#define NOKIA_ENDCOL 131
+
+/* Debug ******************************************************************************/
+
+#ifdef CONFIG_LCD_REGDEBUG
+# define lcddbg(format, arg...) llvdbg(format, ##arg)
+#else
+# define lcddbg(x...)
+#endif
+
+/**************************************************************************************
+ * Private Type Definition
+ **************************************************************************************/
+
+/* This structure describes the state of this driver */
+
+struct nokia_dev_s
+{
+ /* Publically visible device structure */
+
+ struct lcd_dev_s dev;
+
+ /* Private LCD-specific information follows */
+
+ FAR struct spi_dev_s *spi; /* Contained SPI driver instance */
+ uint8_t contrast; /* Current contrast setting */
+ uint8_t power; /* Current power (backlight) setting */
+};
+
+/**************************************************************************************
+ * Private Function Protototypes
+ **************************************************************************************/
+
+/* SPI support */
+
+static inline void nokia_configspi(FAR struct spi_dev_s *spi);
+#ifdef CONFIG_SPI_OWNBUS
+static inline void nokia_select(FAR struct spi_dev_s *spi);
+static inline void nokia_deselect(FAR struct spi_dev_s *spi);
+#else
+static void nokia_select(FAR struct spi_dev_s *spi);
+static void nokia_deselect(FAR struct spi_dev_s *spi);
+#endif
+static void nokia_sndcmd(FAR struct spi_dev_s *spi, const uint8_t cmd);
+static void nokia_cmdarray(FAR struct spi_dev_s *spi, int len, const uint8_t *cmddata);
+static void nokia_clrram(FAR struct spi_dev_s *spi);
+
+/* LCD Data Transfer Methods */
+
+static int nokia_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels);
+static int nokia_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels);
+
+/* LCD Configuration */
+
+static int nokia_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo);
+static int nokia_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+# error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+# error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int nokia_getpower(struct lcd_dev_s *dev);
+static int nokia_setpower(struct lcd_dev_s *dev, int power);
+static int nokia_getcontrast(struct lcd_dev_s *dev);
+static int nokia_setcontrast(struct lcd_dev_s *dev, unsigned int contrast);
+
+/* Initialization */
+
+static int nokia_initialize(struct nokia_dev_s *priv);
+
+/**************************************************************************************
+ * Private Data
+ **************************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane. This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8). Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently. However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+#if CONFIG_NOKIA6100_BPP == 8
+static uint8_t g_runbuffer[NOKIA_XRES];
+#elif CONFIG_NOKIA6100_BPP == 12
+static uint8_t g_runbuffer[(3*NOKIA_XRES+1)/2];
+#else /* CONFIG_NOKIA6100_BPP == 16 */
+static uint16_t g_runbuffer[NOKIA_XRES];
+#endif
+
+/* g_rowbuf is another buffer, but used internally by the Nokia 6100 driver in order
+ * expand the pixel data into 9-bit data needed by the LCD. There are some
+ * customizations that would eliminate the need for this extra buffer and for the
+ * extra expansion/copy, but those customizations would require a special, non-standard
+ * SPI driver that could expand 8- to 9-bit data on the fly.
+ */
+
+static uint16_t g_rowbuf[NOKIA_STRIDE+1];
+
+/* Device Driver Data Structures ******************************************************/
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+ .fmt = NOKIA_COLORFMT, /* Color format: RGB16-565: RRRR RGGG GGGB BBBB */
+ .xres = NOKIA_XRES, /* Horizontal resolution in pixel columns */
+ .yres = NOKIA_YRES, /* Vertical resolution in pixel rows */
+ .nplanes = 1, /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+ .putrun = nokia_putrun, /* Put a run into LCD memory */
+ .getrun = nokia_getrun, /* Get a run from LCD memory */
+ .buffer = (uint8_t*)g_runbuffer, /* Run scratch buffer */
+ .bpp = NOKIA_BPP, /* Bits-per-pixel */
+};
+
+/* This is the standard, NuttX LCD driver object */
+
+static struct nokia_dev_s g_lcddev =
+{
+ .dev =
+ {
+ /* LCD Configuration */
+
+ .getvideoinfo = nokia_getvideoinfo,
+ .getplaneinfo = nokia_getplaneinfo,
+
+ /* LCD RGB Mapping -- Not supported */
+ /* Cursor Controls -- Not supported */
+
+ /* LCD Specific Controls */
+
+ .getpower = nokia_getpower,
+ .setpower = nokia_setpower,
+ .getcontrast = nokia_getcontrast,
+ .setcontrast = nokia_setcontrast,
+ },
+};
+
+/* LCD Command Strings ****************************************************************/
+
+#ifdef CONFIG_NOKIA6100_S1D15G10
+/* Display control:
+ * P1: Specifies the CL dividing ratio, F1 and F2 drive-pattern switching period.
+ * P2: Specifies the duty of the module on block basis
+ * P3: Specify number of lines to be inversely highlighted on LCD panel
+ * P4: 0: Dispersion P40= 1: Non-dispersion
+ */
+
+#if 1 // CONFIG_NOKIA6100_BPP == 12
+static const uint8_t g_disctl[] =
+{
+ S1D15G10_DISCTL, /* Display control */
+ DISCTL_CLDIV_2|DISCTL_PERIOD_8, /* P1: Divide clock by 2; switching period = 8 */
+//DISCTL_CLDIV_NONE|DISCTL_PERIOD_8, /* P1: No clock division; switching period = 8 */
+ 32, /* P2: nlines/4 - 1 = 132/4 - 1 = 32 */
+ 0, /* P3: No inversely highlighted lines */
+ 0 /* P4: No disperion */
+};
+#else /* CONFIG_NOKIA6100_BPP == 8 */
+static const uint8_t g_disctl[] =
+{
+ S1D15G10_DISCTL, /* Display control */
+ DISCTL_CLDIV_2|DISCTL_PERIOD_FLD, /* P1: Divide clock by 2; switching period = field */
+ 32, /* P2: nlines/4 - 1 = 132/4 - 1 = 32 */
+ 0, /* P3: No inversely highlighted lines */
+ 0 /* P4: No disperion */
+};
+#endif
+
+/* Common scan direction:
+ * P1: Cpecify the common output scan direction.
+ */
+
+static const uint8_t g_comscn[] =
+{
+ S1D15G10_COMSCN, /* Common scan direction */
+ 1 /* 0x01 = Scan 1->68, 132<-69 */
+};
+
+/* Power control:
+ * P1: Turn on or off the liquid crystal driving power circuit, booster/step-down
+ * circuits and voltage follower circuit.
+ */
+
+static const uint8_t g_pwrctr[] =
+{
+ S1D15G10_PWRCTR, /* Power control */
+ PWCTR_REFVOLTAGE|PWCTR_REGULATOR|PWCTR_BOOSTER2|PWCTR_BOOSTER1
+};
+
+/* Data control:
+ * P1: Specify the normal or inverse display of the page address and also to specify
+ * the page address scanning direction
+ * P2: RGB sequence
+ * P3: Grayscale setup
+ */
+
+static const uint8_t g_datctl[] =
+{
+ S1D15G10_DATCTL, /* Data control */
+ 0
+#if CONFIG_NOKIA6100_MY != 0 /* Display row direction */
+ |DATCTL_PGADDR_INV /* Page address inverted */
+#endif
+#if CONFIG_NOKIA6100_MX != 0 /* Display column direction */
+ |DATCTL_COLADDR_REV /* Column address reversed */
+#endif
+#if CONFIG_NOKIA6100_V != 0 /* Display address direction */
+ |DATCTL_ADDR_PGDIR /* Address scan in page direction */
+#endif
+ ,
+#if CONFIG_NOKIA6100_RGBORD != 0
+ DATCTL_BGR, /* RGB->BGR */
+#else
+ 0, /* RGB->RGB */
+#endif
+#if CONFIG_NOKIA6100_BPP == 8
+ DATCTL_8GRAY /* Selects 8-bit color */
+#elif CONFIG_NOKIA6100_BPP == 12
+ DATCTL_16GRAY_A /* Selects 16-bit color, Type A */
+#else
+# error "16-bit mode not yet implemented"
+#endif
+};
+
+/* Voltage control (contrast setting):
+ * P1: Volume value
+ * P2: Resistance ratio
+ * (May need to be tuned for individual displays)
+ */
+
+static const uint8_t g_volctr[] =
+{
+ S1D15G10_VOLCTR, /* Volume control */
+ NOKIA_DEFAULT_CONTRAST, /* Volume value */
+ 2 /* Resistance ratio */
+};
+
+/* 256-color position set (RGBSET8) */
+
+#if CONFIG_NOKIA6100_BPP == 8
+static const uint8_t g_rgbset8[] =
+{
+ S1D15G10_RGBSET8, /* 256-color position set */
+ 0, 2, 4, 6, 9, 11, 13, 15, /* Red tones */
+ 0, 2, 4, 6, 9, 11, 13, 15, /* Green tones */
+ 0, 5, 10, 15 /* Blue tones */
+};
+#endif
+
+/* Page address set (PASET) */
+
+static const uint8_t g_paset[] =
+{
+ S1D15G10_PASET, /* Page start address set */
+ NOKIA_PGBIAS,
+ 131
+};
+
+/* Column address set (CASET) */
+
+static const uint8_t g_caset[] =
+{
+ S1D15G10_CASET, /* Column start address set */
+ NOKIA_COLBIAS,
+ 131
+};
+#endif /* CONFIG_NOKIA6100_S1D15G10 */
+
+#ifdef CONFIG_NOKIA6100_PCF8833
+
+/* Color interface pixel format (COLMOD) */
+
+#if CONFIG_NOKIA6100_BPP == 12
+static const uint8_t g_colmod[] =
+{
+ PCF8833_COLMOD, /* Color interface pixel format */
+ PCF8833_FMT_12BPS /* 12 bits-per-pixel */
+};
+#else /* CONFIG_NOKIA6100_BPP == 8 */
+static const uint8_t g_colmod[] =
+{
+ PCF8833_COLMOD, /* Color interface pixel format */
+ PCF8833_FMT_8BPS /* 8 bits-per-pixel */
+};
+#endif
+
+/* Memory data access control(MADCTL) */
+
+static const uint8_t g_madctl[] =
+{
+ PCF8833_MADCTL, /* Memory data access control*/
+ 0
+#ifdef CONFIG_NOKIA6100_RGBORD != 0
+ |MADCTL_RGB /* RGB->BGR */
+#endif
+#ifdef CONFIG_NOKIA6100_MY != 0 /* Display row direction */
+ |MADCTL_MY /* Mirror Y */
+#endif
+#ifdef CONFIG_NOKIA6100_MX != 0 /* Display column direction */
+ |MADCTL_MX /* Mirror X */
+#endif
+#ifdef CONFIG_NOKIA6100_V != 0 /* Display address direction */
+ |MADCTL_V /* ertical RAM write; in Y direction */
+#endif
+#ifdef CONFIG_NOKIA6100_ML != 0 /* Display scan direction */
+ |MADCTL_LAO /* Line address order bottom to top */
+#endif
+};
+
+/* Set contrast (SETCON) */
+
+static const uint8_t g_setcon[] =
+{
+ PCF8833_SETCON, /* Set contrast */
+ NOKIA_DEFAULT_CONTRAST
+};
+
+#endif /* CONFIG_NOKIA6100_PCF8833 */
+
+/**************************************************************************************
+ * Private Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Function: nokia_configspi
+ *
+ * Description:
+ * Configure the SPI for use with the Nokia 6100
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ **************************************************************************************/
+
+static inline void nokia_configspi(FAR struct spi_dev_s *spi)
+{
+ lcddbg("Mode: %d Bits: %d Frequency: %d\n",
+ CONFIG_NOKIA6100_SPIMODE, CONFIG_NOKIA6100_WORDWIDTH, CONFIG_NOKIA6100_FREQUENCY);
+
+ /* Configure SPI for the Nokia 6100. But only if we own the SPI bus. Otherwise, don't
+ * bother because it might change.
+ */
+
+#ifdef CONFIG_SPI_OWNBUS
+ SPI_SETMODE(spi, CONFIG_NOKIA6100_SPIMODE);
+ SPI_SETBITS(spi, CONFIG_NOKIA6100_WORDWIDTH);
+ SPI_SETFREQUENCY(spi, CONFIG_NOKIA6100_FREQUENCY)
+#endif
+}
+
+/**************************************************************************************
+ * Function: nokia_select
+ *
+ * Description:
+ * Select the SPI, locking and re-configuring if necessary
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ **************************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void nokia_select(FAR struct spi_dev_s *spi)
+{
+ /* We own the SPI bus, so just select the chip */
+
+ lcddbg("SELECTED\n");
+ SPI_SELECT(spi, SPIDEV_DISPLAY, true);
+}
+#else
+static void nokia_select(FAR struct spi_dev_s *spi)
+{
+ /* Select Nokia 6100 chip (locking the SPI bus in case there are multiple
+ * devices competing for the SPI bus
+ */
+
+ lcddbg("SELECTED\n");
+ SPI_LOCK(spi, true);
+ SPI_SELECT(spi, SPIDEV_DISPLAY, true);
+
+ /* Now make sure that the SPI bus is configured for the Nokia 6100 (it
+ * might have gotten configured for a different device while unlocked)
+ */
+
+ SPI_SETMODE(spi, CONFIG_NOKIA6100_SPIMODE);
+ SPI_SETBITS(spi, CONFIG_NOKIA6100_WORDWIDTH);
+ SPI_SETFREQUENCY(spi, CONFIG_NOKIA6100_FREQUENCY);
+}
+#endif
+
+/**************************************************************************************
+ * Function: nokia_deselect
+ *
+ * Description:
+ * De-select the SPI
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ **************************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void nokia_deselect(FAR struct spi_dev_s *spi)
+{
+ /* We own the SPI bus, so just de-select the chip */
+
+ lcddbg("DE-SELECTED\n");
+ SPI_SELECT(spi, SPIDEV_DISPLAY, false);
+}
+#else
+static void nokia_deselect(FAR struct spi_dev_s *spi)
+{
+ /* De-select Nokia 6100 chip and relinquish the SPI bus. */
+
+ lcddbg("DE-SELECTED\n");
+ SPI_SELECT(spi, SPIDEV_DISPLAY, false);
+ SPI_LOCK(spi, false);
+}
+#endif
+
+/**************************************************************************************
+ * Name: nokia_sndcmd
+ *
+ * Description:
+ * Send a 1-byte command.
+ *
+ **************************************************************************************/
+
+static void nokia_sndcmd(FAR struct spi_dev_s *spi, const uint8_t cmd)
+{
+ /* Select the LCD */
+
+ lcddbg("cmd: %02x\n", cmd);
+ nokia_select(spi);
+
+ /* Send the command. Bit 8 == 0 denotes a command */
+
+ (void)SPI_SEND(spi, (uint16_t)cmd);
+
+ /* De-select the LCD */
+
+ nokia_deselect(spi);
+}
+
+/**************************************************************************************
+ * Name: nokia_cmddata
+ *
+ * Description:
+ * Send a 1-byte command followed by datlen data bytes.
+ *
+ **************************************************************************************/
+
+static void nokia_cmddata(FAR struct spi_dev_s *spi, uint8_t cmd, int datlen,
+ const uint8_t *data)
+{
+ uint16_t *rowbuf = g_rowbuf;
+ int i;
+
+ lcddbg("cmd: %02x datlen: %d\n", cmd, datlen);
+ DEBUGASSERT(datlen <= NOKIA_STRIDE);
+
+ /* Copy the command into the line buffer. Bit 8 == 0 denotes a command. */
+
+ *rowbuf++ = cmd;
+
+ /* Copy any data after the command into the line buffer */
+
+ for (i = 0; i < datlen; i++)
+ {
+ /* Bit 8 == 1 denotes data */
+
+ *rowbuf++ = (uint16_t)*data++ | NOKIA_LCD_DATA;
+ }
+
+ /* Select the LCD */
+
+ nokia_select(spi);
+
+ /* Send the line buffer. */
+
+ (void)SPI_SNDBLOCK(spi, g_rowbuf, datlen+1);
+
+ /* De-select the LCD */
+
+ nokia_deselect(spi);
+}
+
+/**************************************************************************************
+ * Name: nokia_ramwr
+ *
+ * Description:
+ * Send a RAMWR command followed by datlen data bytes.
+ *
+ **************************************************************************************/
+
+static void nokia_ramwr(FAR struct spi_dev_s *spi, int datlen, const uint8_t *data)
+{
+ nokia_cmddata(spi, LCD_RAMWR, datlen, data);
+}
+
+/**************************************************************************************
+ * Name: nokia_cmdarray
+ *
+ * Description:
+ * Send a RAMWR command followed by len-1 data bytes.
+ *
+ **************************************************************************************/
+
+static void nokia_cmdarray(FAR struct spi_dev_s *spi, int len, const uint8_t *cmddata)
+{
+#ifdef CONFIG_LCD_REGDEBUG
+ int i;
+
+ for (i = 0; i < len; i++)
+ {
+ lcddbg("cmddata[%d]: %02x\n", i, cmddata[i]);
+ }
+#endif
+ nokia_cmddata(spi, cmddata[0], len-1, &cmddata[1]);
+}
+
+/**************************************************************************************
+ * Name: nokia_clrram
+ *
+ * Description:
+ * Send a 1-byte command followed by len-1 data bytes.
+ *
+ **************************************************************************************/
+
+static void nokia_clrram(FAR struct spi_dev_s *spi)
+{
+ uint16_t *rowbuf = g_rowbuf;
+ int i;
+
+ /* Set all zero data in the line buffer */
+
+ for (i = 0; i < NOKIA_STRIDE; i++)
+ {
+ /* Bit 8 == 1 denotes data */
+
+ *rowbuf++ = NOKIA_LCD_DATA;
+ }
+
+ /* Select the LCD and send the RAMWR command */
+
+ nokia_select(spi);
+ SPI_SEND(spi, LCD_RAMWR);
+
+ /* Send the line buffer, once for each row. */
+
+ for (i = 0; i < NOKIA_YRES; i++)
+ {
+ (void)SPI_SNDBLOCK(spi, g_rowbuf, NOKIA_STRIDE);
+ }
+
+ /* De-select the LCD */
+
+ nokia_deselect(spi);
+}
+
+/**************************************************************************************
+ * Name: nokia_putrun
+ *
+ * Description:
+ * This method can be used to write a partial raster line to the LCD:
+ *
+ * row - Starting row to write to (range: 0 <= row < yres)
+ * col - Starting column to write to (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer containing the run to be written to the LCD
+ * npixels - The number of pixels to write to the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+static int nokia_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels)
+{
+ struct nokia_dev_s *priv = &g_lcddev;
+ FAR struct spi_dev_s *spi = priv->spi;
+ uint16_t cmd[3];
+
+ gvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+
+#if NOKIA_XBIAS > 0
+ col += NOKIA_XBIAS;
+#endif
+#if NOKIA_YBIAS > 0
+ row += NOKIA_YBIAS;
+#endif
+ DEBUGASSERT(buffer && col >=0 && (col + npixels) <= NOKIA_XRES && row >= 0 && row < NOKIA_YRES);
+
+ /* Set up to write the run. */
+
+ nokia_select(spi);
+ cmd[0] = LCD_PASET;
+ cmd[1] = col | NOKIA_LCD_DATA;
+ cmd[2] = NOKIA_ENDPAGE | NOKIA_LCD_DATA;
+ (void)SPI_SNDBLOCK(spi, cmd, 3);
+ nokia_deselect(spi);
+
+ /* De-select the LCD */
+
+ nokia_select(spi);
+ cmd[0] = LCD_CASET;
+ cmd[1] = row | NOKIA_LCD_DATA;
+ cmd[2] = NOKIA_ENDCOL | NOKIA_LCD_DATA;
+ (void)SPI_SNDBLOCK(spi, cmd, 3);
+ nokia_deselect(spi);
+
+ /* Then send the run */
+
+ nokia_ramwr(spi, NOKIA_PIX2BYTES(npixels), buffer);
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: nokia_getrun
+ *
+ * Description:
+ * This method can be used to read a partial raster line from the LCD:
+ *
+ * row - Starting row to read from (range: 0 <= row < yres)
+ * col - Starting column to read read (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer in which to return the run read from the LCD
+ * npixels - The number of pixels to read from the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+static int nokia_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels)
+{
+ gvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);
+
+ /* At present, this is a write-only LCD driver */
+
+#warning "Not implemented"
+ return -ENOSYS;
+}
+
+/**************************************************************************************
+ * Name: nokia_getvideoinfo
+ *
+ * Description:
+ * Get information about the LCD video controller configuration.
+ *
+ **************************************************************************************/
+
+static int nokia_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo)
+{
+ DEBUGASSERT(dev && vinfo);
+ gvdbg("fmt: %d xres: %d yres: %d nplanes: %d\n",
+ g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres, g_videoinfo.nplanes);
+ memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: nokia_getplaneinfo
+ *
+ * Description:
+ * Get information about the configuration of each LCD color plane.
+ *
+ **************************************************************************************/
+
+static int nokia_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo)
+{
+ DEBUGASSERT(dev && pinfo && planeno == 0);
+ gvdbg("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+ memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: nokia_getpower
+ *
+ * Description:
+ * Get the LCD panel power status (0: full off - CONFIG_LCD_MAXPOWER: full on. On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int nokia_getpower(struct lcd_dev_s *dev)
+{
+ struct nokia_dev_s *priv = (struct nokia_dev_s *)dev;
+ gvdbg("power: %d\n", priv->power);
+ return priv->power;
+}
+
+/**************************************************************************************
+ * Name: nokia_setpower
+ *
+ * Description:
+ * Enable/disable LCD panel power (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int nokia_setpower(struct lcd_dev_s *dev, int power)
+{
+ struct nokia_dev_s *priv = (struct nokia_dev_s *)dev;
+ int ret;
+
+ gvdbg("power: %d\n", power);
+ DEBUGASSERT(power <= CONFIG_LCD_MAXPOWER);
+
+ /* Set new power level. The backlight power is controlled outside of the LCD
+ * assembly and must be managmed by board-specific logic.
+ */
+
+ ret = nokia_backlight(power);
+ if (ret == OK)
+ {
+ priv->power = power;
+ }
+ return ret;
+}
+
+/**************************************************************************************
+ * Name: nokia_getcontrast
+ *
+ * Description:
+ * Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int nokia_getcontrast(struct lcd_dev_s *dev)
+{
+ struct nokia_dev_s *priv = (struct nokia_dev_s *)dev;
+ gvdbg("contrast: %d\n", priv->contrast);
+ return priv->contrast;
+}
+
+/**************************************************************************************
+ * Name: nokia_setcontrast
+ *
+ * Description:
+ * Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int nokia_setcontrast(struct lcd_dev_s *dev, unsigned int contrast)
+{
+ struct nokia_dev_s *priv = (struct nokia_dev_s *)dev;
+
+ if (contrast < CONFIG_LCD_MAXCONTRAST)
+ {
+#ifdef CONFIG_NOKIA6100_S1D15G10
+ while (priv->contrast < contrast)
+ {
+ nokia_sndcmd(priv->spi, S1D15G10_VOLUP);
+ priv->contrast++;
+ }
+ while (priv->contrast > contrast)
+ {
+ nokia_sndcmd(priv->spi, S1D15G10_VOLDOWN);
+ priv->contrast--;
+ }
+#else /* CONFIG_NOKIA6100_PCF8833 */
+ uint8_t cmd[2];
+
+ cmd[0] = PCF8833_SETCON;
+ cmd[1] = priv->contrast;
+ nokia_sndarry(priv->spi, 2, cmd);
+ priv->contrast = contrast;
+#endif
+ }
+
+ gvdbg("contrast: %d\n", contrast);
+ return -ENOSYS;
+}
+
+/**************************************************************************************
+ * Name: nokia_initialize
+ *
+ * Description:
+ * Initialize the LCD controller.
+ *
+ **************************************************************************************/
+
+#ifdef CONFIG_NOKIA6100_S1D15G10
+static int nokia_initialize(struct nokia_dev_s *priv)
+{
+ struct spi_dev_s *spi = priv->spi;
+
+ /* Configure the display */
+
+ nokia_cmdarray(spi, sizeof(g_disctl), g_disctl); /* Display control */
+ nokia_cmdarray(spi, sizeof(g_comscn), g_comscn); /* Common scan direction */
+ nokia_sndcmd(spi, S1D15G10_OSCON); /* Internal oscilator ON */
+ nokia_sndcmd(spi, S1D15G10_SLPOUT); /* Sleep out */
+ nokia_cmdarray(spi, sizeof(g_volctr), g_volctr); /* Volume control (contrast) */
+ nokia_cmdarray(spi, sizeof(g_pwrctr), g_pwrctr); /* Turn on voltage regulators */
+ up_mdelay(100);
+#ifdef CONFIG_NOKIA6100_INVERT
+ nokia_sndcmd(spi, S1D15G10_DISINV); /* Invert display */
+#else
+ nokia_sndcmd(spi, S1D15G10_DISNOR); /* Normal display */
+#endif
+ nokia_cmdarray(spi, sizeof(g_datctl), g_datctl); /* Data control */
+#if CONFIG_NOKIA6100_BPP == 8
+ nokia_cmdarray(spi, sizeof(g_rgbset8), g_rgbset8); /* Set up color lookup table */
+ nokia_sndcmd(spi, S1D15G10_NOP);
+#endif
+ nokia_cmdarray(spi, sizeof(g_paset), g_paset); /* Page address set */
+ nokia_cmdarray(spi, sizeof(g_paset), g_caset); /* Column address set */
+ nokia_clrram(spi);
+ nokia_sndcmd(spi, S1D15G10_DISON); /* Display on */
+ return OK;
+}
+#endif
+
+#ifdef CONFIG_NOKIA6100_PCF8833
+static int nokia_initialize(struct nokia_dev_s *priv)
+{
+ struct struct spi_dev_s *spi = priv->spi;
+
+ nokia_sndcmd(spi, PCF8833_SLEEPOUT); /* Exit sleep mode */
+ nokia_sndcmd(spi, PCF8833_BSTRON); /* Turn on voltage booster */
+#ifdef CONFIG_NOKIA6100_INVERT
+ nokia_sndcmd(spi, PCF8833_INVON); /* Invert display */
+#else
+ nokia_sndcmd(spi, PCF8833_INVOFF); /* Don't invert display */
+#endif
+ nokia_cmdarray(spi, sizeof(g_madctl), g_madctl); /* Memory data access control */
+ nokia_cmdarray(spi, sizeof(g_colmod), g_colmod); /* Color interface pixel format */
+ nokia_cmdarray(spi, sizeof(g_setcon), g_setcon); /* Set contrast */
+ nokia_sndcmd(spi, PCF8833_NOP); /* No operation */
+ nokia_clrram(spi);
+ nokia_sndcmd(spi, PCF8833_DISPON); /* Display on */
+ return OK;
+}
+#endif /* CONFIG_NOKIA6100_PCF8833 */
+
+/**************************************************************************************
+ * Public Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: nokia_lcdinitialize
+ *
+ * Description:
+ * Initialize the NOKIA6100 video hardware. The initial state of the LCD is fully
+ * initialized, display memory cleared, and the LCD ready to use, but with the power
+ * setting at 0 (full off == sleep mode).
+ *
+ * Input Parameters:
+ *
+ * spi - A reference to the SPI driver instance.
+ * devno - A value in the range of 0 throuh CONFIG_NOKIA6100_NINTERFACES-1. This
+ * allows support for multiple LCD devices.
+ *
+ * Returned Value:
+ *
+ * On success, this function returns a reference to the LCD object for the specified
+ * LCD. NULL is returned on any failure.
+ *
+ **************************************************************************************/
+
+FAR struct lcd_dev_s *nokia_lcdinitialize(FAR struct spi_dev_s *spi, unsigned int devno)
+{
+ struct nokia_dev_s *priv = &g_lcddev;
+
+ gvdbg("Initializing\n");
+ DEBUGASSERT(devno == 0);
+
+ /* Initialize the driver data structure */
+
+ priv->spi = spi; /* Save the SPI instance */
+ priv->contrast = NOKIA_DEFAULT_CONTRAST; /* Initial contrast setting */
+
+ /* Configure and enable the LCD controller */
+
+ nokia_configspi(spi);
+ if (nokia_initialize(priv) == OK)
+ {
+ /* Turn on the backlight */
+
+ nokia_backlight(CONFIG_NOKIA6100_BLINIT);
+ return &priv->dev;
+ }
+ return NULL;
+}
diff --git a/nuttx/drivers/lcd/p14201.c b/nuttx/drivers/lcd/p14201.c
new file mode 100644
index 000000000..934d251ca
--- /dev/null
+++ b/nuttx/drivers/lcd/p14201.c
@@ -0,0 +1,1246 @@
+/**************************************************************************************
+ * drivers/lcd/p14201.c
+ * Driver for RiT P14201 series display (wih sd1329 IC controller)
+ *
+ * Copyright (C) 2010, 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Included Files
+ **************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/p14201.h>
+
+#include <arch/irq.h>
+
+#include "sd1329.h"
+
+#ifdef CONFIG_LCD_P14201
+
+/**************************************************************************************
+ * Pre-processor Definitions
+ **************************************************************************************/
+
+/* Configuration **********************************************************************/
+
+/* P14201 Configuration Settings:
+ *
+ * CONFIG_P14201_SPIMODE - Controls the SPI mode
+ * CONFIG_P14201_FREQUENCY - Define to use a different bus frequency
+ * CONFIG_P14201_NINTERFACES - Specifies the number of physical P14201 devices that
+ * will be supported.
+ * CONFIG_P14201_FRAMEBUFFER - If defined, accesses will be performed using an in-memory
+ * copy of the OLEDs GDDRAM. This cost of this buffer is 128 * 96 / 2 = 6Kb. If this
+ * is defined, then the driver will be fully functional. If not, then it will have the
+ * following limitations:
+ *
+ * - Reading graphics memory cannot be supported, and
+ * - All pixel writes must be aligned to byte boundaries.
+ *
+ * The latter limitation effectively reduces the 128x96 disply to 64x96.
+ *
+ * Required LCD driver settings:
+ * CONFIG_LCD_P14201 - Enable P14201 support
+ * CONFIG_LCD_MAXCONTRAST should be 255, but any value >0 and <=255 will be accepted.
+ * CONFIG_LCD_MAXPOWER must be 1
+ *
+ * Required SPI driver settings:
+ * CONFIG_SPI_CMDDATA - Include support for cmd/data selection.
+ */
+
+#ifndef CONFIG_SPI_CMDDATA
+# error "CONFIG_SPI_CMDDATA must be defined in your NuttX configuration"
+#endif
+
+/* The P14201 spec says that is supports SPI mode 0,0 only. However,
+ * somtimes you need to tinker with these things.
+ */
+
+#ifndef CONFIG_P14201_SPIMODE
+# define CONFIG_P14201_SPIMODE SPIDEV_MODE2
+#endif
+
+/* CONFIG_P14201_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_P14201_NINTERFACES
+# define CONFIG_P14201_NINTERFACES 1
+#endif
+
+#if CONFIG_P14201_NINTERFACES != 1
+# error "This implementation supports only a single OLED device"
+#endif
+
+/* Check contrast selection */
+
+#if !defined(CONFIG_LCD_MAXCONTRAST)
+# define CONFIG_LCD_MAXCONTRAST 255
+#endif
+
+#if CONFIG_LCD_MAXCONTRAST <= 0|| CONFIG_LCD_MAXCONTRAST > 255
+# error "CONFIG_LCD_MAXCONTRAST exceeds supported maximum"
+#endif
+
+/* Check power setting */
+
+#if !defined(CONFIG_LCD_MAXPOWER)
+# define CONFIG_LCD_MAXPOWER 1
+#endif
+
+#if CONFIG_LCD_MAXPOWER != 1
+# warning "CONFIG_LCD_MAXPOWER exceeds supported maximum"
+# undef CONFIG_LCD_MAXPOWER
+# define CONFIG_LCD_MAXPOWER 1
+#endif
+
+/* Color is 4bpp greyscale with leftmost column contained in bits 7:4 */
+
+#if defined(CONFIG_NX_DISABLE_4BPP) || !defined(CONFIG_NX_PACKEDMSFIRST)
+# warning "4-bit, big-endian pixel support needed"
+#endif
+
+/* Define the CONFIG_LCD_RITDEBUG to enable detailed debug output (stuff you would
+ * never want to see unless you are debugging this file).
+ *
+ * Verbose debug must also be enabled
+ */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_DEBUG_GRAPHICS
+#endif
+
+#ifndef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_LCD_RITDEBUG
+#endif
+
+/* Color Properties *******************************************************************/
+
+/* Display Resolution */
+
+#define RIT_XRES 128
+#define RIT_YRES 96
+
+/* Color depth and format */
+
+#define RIT_BPP 4
+#define RIT_COLORFMT FB_FMT_Y4
+
+/* Default contrast */
+
+#define RIT_CONTRAST ((23 * (CONFIG_LCD_MAXCONTRAST+1) / 32) - 1)
+
+/* Helper Macros **********************************************************************/
+
+#define rit_sndcmd(p,b,l) rit_sndbytes(p,b,l,true);
+#define rit_snddata(p,b,l) rit_sndbytes(p,b,l,false);
+
+/* Debug ******************************************************************************/
+
+#ifdef CONFIG_LCD_RITDEBUG
+# define ritdbg(format, arg...) vdbg(format, ##arg)
+#else
+# define ritdbg(x...)
+#endif
+
+/**************************************************************************************
+ * Private Type Definition
+ **************************************************************************************/
+
+/* This structure describes the state of this driver */
+
+struct rit_dev_s
+{
+ struct lcd_dev_s dev; /* Publically visible device structure */
+ FAR struct spi_dev_s *spi; /* Cached SPI device reference */
+ uint8_t contrast; /* Current contrast setting */
+ bool on; /* true: display is on */
+};
+
+/**************************************************************************************
+ * Private Function Protototypes
+ **************************************************************************************/
+
+/* Low-level SPI helpers */
+
+static inline void rit_configspi(FAR struct spi_dev_s *spi);
+#ifdef CONFIG_SPI_OWNBUS
+static inline void rit_select(FAR struct spi_dev_s *spi);
+static inline void rit_deselect(FAR struct spi_dev_s *spi);
+#else
+static void rit_select(FAR struct spi_dev_s *spi);
+static void rit_deselect(FAR struct spi_dev_s *spi);
+#endif
+static void rit_sndbytes(FAR struct rit_dev_s *priv, FAR const uint8_t *buffer,
+ size_t buflen, bool cmd);
+static void rit_sndcmds(FAR struct rit_dev_s *priv, FAR const uint8_t *table);
+
+/* LCD Data Transfer Methods */
+
+static int rit_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels);
+static int rit_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels);
+
+/* LCD Configuration */
+
+static int rit_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo);
+static int rit_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+# error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+# error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int rit_getpower(struct lcd_dev_s *dev);
+static int rit_setpower(struct lcd_dev_s *dev, int power);
+static int rit_getcontrast(struct lcd_dev_s *dev);
+static int rit_setcontrast(struct lcd_dev_s *dev, unsigned int contrast);
+
+/**************************************************************************************
+ * Private Data
+ **************************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane. This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8). Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently. However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[RIT_XRES / 2];
+
+/* CONFIG_P14201_FRAMEBUFFER - If defined, accesses will be performed using an in-memory
+ * copy of the OLEDs GDDRAM. This cost of this buffer is 128 * 64 / 2 = 4Kb. If this
+ * is defined, then the driver will be full functional. If not, then:
+ *
+ * - Reading graphics memory cannot be supported, and
+ * - All pixel writes must be aligned to byte boundaries.
+ */
+
+#ifdef CONFIG_P14201_FRAMEBUFFER
+static uint8_t g_framebuffer[RIT_YRES * RIT_XRES / 2];
+#endif
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+ .fmt = RIT_COLORFMT, /* Color format: RGB16-565: RRRR RGGG GGGB BBBB */
+ .xres = RIT_XRES, /* Horizontal resolution in pixel columns */
+ .yres = RIT_YRES, /* Vertical resolution in pixel rows */
+ .nplanes = 1, /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+ .putrun = rit_putrun, /* Put a run into LCD memory */
+ .getrun = rit_getrun, /* Get a run from LCD memory */
+ .buffer = (uint8_t*)g_runbuffer, /* Run scratch buffer */
+ .bpp = RIT_BPP, /* Bits-per-pixel */
+};
+
+/* This is the OLED driver instance (only a single device is supported for now) */
+
+static struct rit_dev_s g_oleddev =
+{
+ .dev =
+ {
+ /* LCD Configuration */
+
+ .getvideoinfo = rit_getvideoinfo,
+ .getplaneinfo = rit_getplaneinfo,
+
+ /* LCD RGB Mapping -- Not supported */
+ /* Cursor Controls -- Not supported */
+
+ /* LCD Specific Controls */
+
+ .getpower = rit_getpower,
+ .setpower = rit_setpower,
+ .getcontrast = rit_getcontrast,
+ .setcontrast = rit_setcontrast,
+ },
+};
+
+/* A table of magic initialization commands. This initialization sequence is
+ * derived from RiT Application Note for the P14201 (with a few tweaked values
+ * as discovered in some Luminary code examples).
+ */
+
+static const uint8_t g_initcmds[] =
+{
+ 3, SSD1329_CMD_LOCK, /* Set lock command */
+ SSD1329_LOCK_OFF, /* Disable locking */
+ SSD1329_NOOP,
+ 2, SSD1329_SLEEP_ON, /* Matrix display OFF */
+ SSD1329_NOOP,
+ 3, SSD1329_ICON_ALL, /* Set all ICONs to OFF */
+ SSD1329_ICON_OFF, /* OFF selection */
+ SSD1329_NOOP,
+ 3, SSD1329_MUX_RATIO, /* Set MUX ratio */
+ 95, /* 96 MUX */
+ SSD1329_NOOP,
+ 3, SSD1329_SET_CONTRAST, /* Set contrast */
+ RIT_CONTRAST, /* Default contrast */
+ SSD1329_NOOP,
+ 3, SSD1329_PRECHRG2_SPEED, /* Set second pre-charge speed */
+ (31 << 1) | SSD1329_PRECHRG2_DBL, /* Pre-charge speed == 32, doubled */
+ SSD1329_NOOP,
+ 3, SSD1329_GDDRAM_REMAP, /* Set GDDRAM re-map */
+ (SSD1329_COM_SPLIT| /* Enable COM slip even/odd */
+ SSD1329_COM_REMAP| /* Enable COM re-map */
+ SSD1329_NIBBLE_REMAP), /* Enable nibble re-map */
+ SSD1329_NOOP,
+ 3, SSD1329_VERT_START, /* Set Display Start Line */
+ 0, /* Line = 0 */
+ SSD1329_NOOP,
+ 3, SSD1329_VERT_OFFSET, /* Set Display Offset */
+ 0, /* Offset = 0 */
+ SSD1329_NOOP,
+ 2, SSD1329_DISP_NORMAL, /* Display mode normal */
+ SSD1329_NOOP,
+ 3, SSD1329_PHASE_LENGTH, /* Set Phase Length */
+ 1 | /* Phase 1 period = 1 DCLK */
+ (1 << 4), /* Phase 2 period = 1 DCLK */
+ SSD1329_NOOP,
+ 3, SSD1329_FRAME_FREQ,
+ 35, /* 35 DCLK's per row */
+ SSD1329_NOOP,
+ 3, SSD1329_DCLK_DIV, /* Set Front Clock Divider / Oscillator Frequency */
+ 2 | /* Divide ration = 3 */
+ (14 << 4), /* Oscillator Frequency, FOSC, setting */
+ SSD1329_NOOP,
+ 17, SSD1329_GSCALE_LOOKUP, /* Look Up Table for Gray Scale Pulse width */
+ 1, 2, 3, 4, 5, /* Value for GS1-5 level Pulse width */
+ 6, 8, 10, 12, 14, /* Value for GS6-10 level Pulse width */
+ 16, 19, 22, 26, 30, /* Value for GS11-15 level Pulse width */
+ SSD1329_NOOP,
+ 3, SSD1329_PRECHRG2_PERIOD, /* Set Second Pre-charge Period */
+ 1, /* 1 DCLK */
+ SSD1329_NOOP,
+ 3, SSD1329_PRECHRG1_VOLT, /* Set First Precharge voltage, VP */
+ 0x3f, /* 1.00 x Vcc */
+ SSD1329_NOOP,
+ 0 /* Zero length command terminates table */
+};
+
+/* Turn the maxtrix display on (sleep mode off) */
+
+static const uint8_t g_sleepoff[] =
+{
+ SSD1329_SLEEP_OFF, /* Matrix display ON */
+ SSD1329_NOOP,
+};
+
+/* Turn the maxtrix display off (sleep mode on) */
+
+static const uint8_t g_sleepon[] =
+{
+ SSD1329_SLEEP_ON, /* Matrix display OFF */
+ SSD1329_NOOP,
+};
+
+/* Set horizontal increment mode */
+
+static const uint8_t g_horzinc[] =
+{
+ SSD1329_GDDRAM_REMAP,
+ (SSD1329_COM_SPLIT|SSD1329_COM_REMAP|SSD1329_NIBBLE_REMAP),
+};
+
+/* The following set a window that covers the entire display */
+
+static const uint8_t g_setallcol[] =
+{
+ SSD1329_SET_COLADDR,
+ 0,
+ (RIT_XRES/2)-1
+};
+
+static const uint8_t g_setallrow[] =
+{
+ SSD1329_SET_ROWADDR,
+ 0,
+ RIT_YRES-1
+};
+
+/**************************************************************************************
+ * Private Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: rit_configspi
+ *
+ * Description:
+ * Configure the SPI for use with the P14201
+ *
+ * Input Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ **************************************************************************************/
+
+static inline void rit_configspi(FAR struct spi_dev_s *spi)
+{
+#ifdef CONFIG_P14201_FREQUENCY
+ ritdbg("Mode: %d Bits: 8 Frequency: %d\n",
+ CONFIG_P14201_SPIMODE, CONFIG_P14201_FREQUENCY);
+#else
+ ritdbg("Mode: %d Bits: 8\n", CONFIG_P14201_SPIMODE);
+#endif
+
+ /* Configure SPI for the P14201. But only if we own the SPI bus. Otherwise, don't
+ * bother because it might change.
+ */
+
+#ifdef CONFIG_SPI_OWNBUS
+ SPI_SETMODE(spi, CONFIG_P14201_SPIMODE);
+ SPI_SETBITS(spi, 8);
+#ifdef CONFIG_P14201_FREQUENCY
+ SPI_SETFREQUENCY(spi, CONFIG_P14201_FREQUENCY)
+#endif
+#endif
+}
+
+/**************************************************************************************
+ * Name: rit_select
+ *
+ * Description:
+ * Select the SPI, locking and re-configuring if necessary
+ *
+ * Input Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ **************************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void rit_select(FAR struct spi_dev_s *spi)
+{
+ /* We own the SPI bus, so just select the chip */
+
+ SPI_SELECT(spi, SPIDEV_DISPLAY, true);
+}
+#else
+static void rit_select(FAR struct spi_dev_s *spi)
+{
+ /* Select P14201 chip (locking the SPI bus in case there are multiple
+ * devices competing for the SPI bus
+ */
+
+ SPI_LOCK(spi, true);
+ SPI_SELECT(spi, SPIDEV_DISPLAY, true);
+
+ /* Now make sure that the SPI bus is configured for the P14201 (it
+ * might have gotten configured for a different device while unlocked)
+ */
+
+ SPI_SETMODE(spi, CONFIG_P14201_SPIMODE);
+ SPI_SETBITS(spi, 8);
+#ifdef CONFIG_P14201_FREQUENCY
+ SPI_SETFREQUENCY(spi, CONFIG_P14201_FREQUENCY);
+#endif
+}
+#endif
+
+/**************************************************************************************
+ * Name: rit_deselect
+ *
+ * Description:
+ * De-select the SPI
+ *
+ * Input Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ **************************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void rit_deselect(FAR struct spi_dev_s *spi)
+{
+ /* We own the SPI bus, so just de-select the chip */
+
+ SPI_SELECT(spi, SPIDEV_DISPLAY, false);
+}
+#else
+static void rit_deselect(FAR struct spi_dev_s *spi)
+{
+ /* De-select P14201 chip and relinquish the SPI bus. */
+
+ SPI_SELECT(spi, SPIDEV_DISPLAY, false);
+ SPI_LOCK(spi, false);
+}
+#endif
+
+/**************************************************************************************
+ * Name: rit_sndbytes
+ *
+ * Description:
+ * Send a sequence of command or data bytes to the SSD1329 controller.
+ *
+ * Input Parameters:
+ * spi - Reference to the SPI driver structure
+ * buffer - A reference to memory containing the command bytes to be sent.
+ * buflen - The number of command bytes in buffer to be sent
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The caller as selected the OLED device.
+ *
+ **************************************************************************************/
+
+static void rit_sndbytes(FAR struct rit_dev_s *priv, FAR const uint8_t *buffer,
+ size_t buflen, bool cmd)
+{
+ FAR struct spi_dev_s *spi = priv->spi;
+ uint8_t tmp;
+
+ ritdbg("buflen: %d cmd: %s [%02x %02x %02x]\n",
+ buflen, cmd ? "YES" : "NO", buffer[0], buffer[1], buffer[2] );
+ DEBUGASSERT(spi);
+
+ /* Clear/set the D/Cn bit to enable command or data mode */
+
+ (void)SPI_CMDDATA(spi, SPIDEV_DISPLAY, cmd);
+
+ /* Loop until the entire command/data block is transferred */
+
+ while (buflen-- > 0)
+ {
+ /* Write the next byte to the controller */
+
+ tmp = *buffer++;
+ (void)SPI_SEND(spi, tmp);
+ }
+}
+
+/**************************************************************************************
+ * Name: rit_sndcmd
+ *
+ * Description:
+ * Send multiple commands from a table of commands.
+ *
+ * Input Parameters:
+ * spi - Reference to the SPI driver structure
+ * table - A reference to table containing all of the commands to be sent.
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ **************************************************************************************/
+
+static void rit_sndcmds(FAR struct rit_dev_s *priv, FAR const uint8_t *table)
+{
+ int cmdlen;
+
+ /* Table terminates with a zero length command */
+
+ while ((cmdlen = *table++) != 0)
+ {
+ ritdbg("command: %02x cmdlen: %d\n", *table, cmdlen);
+ rit_sndcmd(priv, table, cmdlen);
+ table += cmdlen;
+ }
+}
+
+/**************************************************************************************
+ * Name: rit_clear
+ *
+ * Description:
+ * This method can be used to clear the entire display.
+ *
+ * Input Parameters:
+ * priv - Reference to private driver structure
+ *
+ * Assumptions:
+ * Caller has selected the OLED section.
+ *
+ **************************************************************************************/
+
+#ifdef CONFIG_P14201_FRAMEBUFFER
+static inline void rit_clear(FAR struct rit_dev_s *priv)
+{
+ FAR uint8_t *ptr = g_framebuffer;
+ unsigned int row;
+
+ ritdbg("Clear display\n");
+
+ /* Initialize the framebuffer */
+
+ memset(g_framebuffer, (RIT_Y4_BLACK << 4) | RIT_Y4_BLACK, RIT_YRES * RIT_XRES / 2);
+
+ /* Set a window to fill the entire display */
+
+ rit_sndcmd(priv, g_setallcol, sizeof(g_setallcol));
+ rit_sndcmd(priv, g_setallrow, sizeof(g_setallrow));
+ rit_sndcmd(priv, g_horzinc, sizeof(g_horzinc));
+
+ /* Display each row */
+
+ for(row = 0; row < RIT_YRES; row++)
+ {
+ /* Display a horizontal run */
+
+ rit_snddata(priv, ptr, RIT_XRES / 2);
+ ptr += RIT_XRES / 2;
+ }
+}
+#else
+static inline void rit_clear(FAR struct rit_dev_s *priv)
+{
+ unsigned int row;
+
+ ritdbg("Clear display\n");
+
+ /* Create a black row */
+
+ memset(g_runbuffer, (RIT_Y4_BLACK << 4) | RIT_Y4_BLACK, RIT_XRES / 2);
+
+ /* Set a window to fill the entire display */
+
+ rit_sndcmd(priv, g_setallcol, sizeof(g_setallcol));
+ rit_sndcmd(priv, g_setallrow, sizeof(g_setallrow));
+ rit_sndcmd(priv, g_horzinc, sizeof(g_horzinc));
+
+ /* Display each row */
+
+ for(row = 0; row < RIT_YRES; row++)
+ {
+ /* Display a horizontal run */
+
+ rit_snddata(priv, g_runbuffer, RIT_XRES / 2);
+ }
+}
+#endif
+
+/**************************************************************************************
+ * Name: rit_putrun
+ *
+ * Description:
+ * This method can be used to write a partial raster line to the LCD.
+ *
+ * Input Parameters:
+ * row - Starting row to write to (range: 0 <= row < yres)
+ * col - Starting column to write to (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer containing the run to be written to the LCD
+ * npixels - The number of pixels to write to the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+#ifdef CONFIG_P14201_FRAMEBUFFER
+static int rit_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels)
+{
+ FAR struct rit_dev_s *priv = (FAR struct rit_dev_s *)&g_oleddev;
+ uint8_t cmd[3];
+ uint8_t *run;
+ int start;
+ int end;
+ int aend;
+ int i;
+
+ ritdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer);
+
+ /* Toss out the special case of the empty run now */
+
+ if (npixels < 1)
+ {
+ return OK;
+ }
+
+ /* Get the beginning of the line containing run in the framebuffer */
+
+ run = g_framebuffer + row * RIT_XRES / 2;
+
+ /* Get the starting and ending byte offsets containing the run.
+ * the run starts at &run[start] and continues through run[end-1].
+ * However, the first and final pixels at these locations may
+ * not be byte aligned.
+ */
+
+ start = col >> 1;
+ aend = (col + npixels) >> 1;
+ end = (col + npixels + 1) >> 1;
+ ritdbg("start: %d aend: %d end: %d\n", start, aend, end);
+
+ /* Copy the run into the framebuffer, handling nibble alignment.
+ *
+ * CASE 1: First pixel X position is byte aligned
+ *
+ * example col=6 npixels = 8 example col=6 npixels=7
+ *
+ * Run: |AB|AB|AB|AB| |AB|AB|AB|AB|
+ * GDDRAM row:
+ * Byte | 0| 1| 2| 3| 4| 5| 6| | 0| 1| 2| 3| 4| 5| 6|
+ * Pixel: |--|--|--|AB|AB|AB|AB| |--|--|--|AB|AB|AB|A-|
+ *
+ * start = 3 start = 3
+ * aend = 6 aend = 6
+ * end = 6 end = 7
+ *
+ */
+
+ if ((col & 1) == 0)
+ {
+ /* Check for the special case of only 1 pixel being blitted */
+
+ if (npixels > 1)
+ {
+ /* Beginning of buffer is properly aligned, from start to aend */
+
+ memcpy(&run[start], buffer, aend - start);
+ }
+
+ /* An even number of byte-aligned pixel pairs have been written (where
+ * zero counts as an even number). If npixels was was odd (including
+ * npixels == 1), then handle the final, byte aligned pixel.
+ */
+
+ if (aend != end)
+ {
+ /* The leftmost column is contained in source bits 7:4 and in
+ * destination bits 7:4
+ */
+
+ run[aend] = (run[aend] & 0x0f) | (buffer[aend - start] & 0xf0);
+ }
+ }
+
+ /* CASE 2: First pixel X position is byte aligned
+ *
+ * example col=7 npixels = 8 example col=7 npixels=7
+ *
+ * Run: |AB|AB|AB|AB| |AB|AB|AB|AB|
+ * GDDRAM row:
+ * Byte | 0| 1| 2| 3| 4| 5| 6| 7| | 0| 1| 2| 3| 4| 5| 6|
+ * Pixel: |--|--|--|-A|BA|BA|BA|B-| |--|--|--|-A|BA|BA|BA|
+ *
+ * start = 3 start = 3
+ * aend = 7 aend = 7
+ * end = 8 end = 7
+ */
+
+ else
+ {
+ uint8_t curr = buffer[0];
+ uint8_t last;
+
+ /* Handle the initial unaligned pixel. Source bits 7:4 into
+ * destination bits 3:0. In the special case of npixel == 1,
+ * this finished the job.
+ */
+
+ run[start] = (run[start] & 0xf0) | (curr >> 4);
+
+ /* Now construct the rest of the bytes in the run (possibly special
+ * casing the final, partial byte below).
+ */
+
+ for (i = start + 1; i < aend; i++)
+ {
+ /* bits 3:0 from previous byte to run bits 7:4;
+ * bits 7:4 of current byte to run bits 3:0
+ */
+
+ last = curr;
+ curr = buffer[i-start];
+ run[i] = (last << 4) | (curr >> 4);
+ }
+
+ /* An odd number of unaligned pixel have been written (where npixels
+ * may have been as small as one). If npixels was was even, then handle
+ * the final, unaligned pixel.
+ */
+
+ if (aend != end)
+ {
+ /* The leftmost column is contained in source bits 3:0 and in
+ * destination bits 7:4
+ */
+
+ run[aend] = (run[aend] & 0x0f) | (curr << 4);
+ }
+ }
+
+ /* Select the SD1329 controller */
+
+ rit_select(priv->spi);
+
+ /* Setup a window that describes a run starting at the specified column
+ * and row, and ending at the column + npixels on the same row.
+ */
+
+ cmd[0] = SSD1329_SET_COLADDR;
+ cmd[1] = start;
+ cmd[2] = end - 1;
+ rit_sndcmd(priv, cmd, 3);
+
+ cmd[0] = SSD1329_SET_ROWADDR;
+ cmd[1] = row;
+ cmd[2] = row;
+ rit_sndcmd(priv, cmd, 3);
+
+ /* Write the run to GDDRAM. */
+
+ rit_sndcmd(priv, g_horzinc, sizeof(g_horzinc));
+ rit_snddata(priv, &run[start], end - start);
+
+ /* De-select the SD1329 controller */
+
+ rit_deselect(priv->spi);
+ return OK;
+}
+#else
+static int rit_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels)
+{
+ FAR struct rit_dev_s *priv = (FAR struct rit_dev_s *)&g_oleddev;
+ uint8_t cmd[3];
+
+ ritdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer);
+
+ if (npixels > 0)
+ {
+ /* Check that the X and Y coordinates are within range */
+
+ DEBUGASSERT(col < RIT_XRES && (col + npixels) <= RIT_XRES && row < RIT_YRES);
+
+ /* Check that the X coordinates are aligned to 8-bit boundaries
+ * (this needs to get fixed somehow)
+ */
+
+ DEBUGASSERT((col & 1) == 0 && (npixels & 1) == 0);
+
+ /* Select the SD1329 controller */
+
+ rit_select(priv->spi);
+
+ /* Setup a window that describes a run starting at the specified column
+ * and row, and ending at the column + npixels on the same row.
+ */
+
+ cmd[0] = SSD1329_SET_COLADDR;
+ cmd[1] = col >> 1;
+ cmd[2] = ((col + npixels) >> 1) - 1;
+ rit_sndcmd(priv, cmd, 3);
+
+ cmd[0] = SSD1329_SET_ROWADDR;
+ cmd[1] = row;
+ cmd[2] = row;
+ rit_sndcmd(priv, cmd, 3);
+
+ /* Write the run to GDDRAM. */
+
+ rit_sndcmd(priv, g_horzinc, sizeof(g_horzinc));
+ rit_snddata(priv, buffer, npixels >> 1);
+
+ /* De-select the SD1329 controller */
+
+ rit_deselect(priv->spi);
+ }
+
+ return OK;
+}
+#endif
+
+/**************************************************************************************
+ * Name: rit_getrun
+ *
+ * Description:
+ * This method can be used to read a partial raster line from the LCD:
+ *
+ * row - Starting row to read from (range: 0 <= row < yres)
+ * col - Starting column to read read (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer in which to return the run read from the LCD
+ * npixels - The number of pixels to read from the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+#ifdef CONFIG_P14201_FRAMEBUFFER
+static int rit_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels)
+{
+ uint8_t *run;
+ int start;
+ int end;
+ int aend;
+ int i;
+
+ ritdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer);
+
+ /* Can't read from OLED GDDRAM in SPI mode, but we can read from the framebuffer */
+ /* Toss out the special case of the empty run now */
+
+ if (npixels < 1)
+ {
+ return OK;
+ }
+
+ /* Get the beginning of the line containing run in the framebuffer */
+
+ run = g_framebuffer + row * RIT_XRES / 2;
+
+ /* Get the starting and ending byte offsets containing the run.
+ * the run starts at &run[start] and continues through run[end-1].
+ * However, the first and final pixels at these locations may
+ * not be byte aligned (see examples in putrun()).
+ */
+
+ start = col >> 1;
+ aend = (col + npixels) >> 1;
+ end = (col + npixels + 1) >> 1;
+
+ /* Copy the run into the framebuffer, handling nibble alignment */
+
+ if ((col & 1) == 0)
+ {
+ /* Check for the special case of only 1 pixels being copied */
+
+ if (npixels > 1)
+ {
+ /* Beginning of buffer is properly aligned, from start to aend */
+
+ memcpy(buffer, &run[start], aend - start + 1);
+ }
+
+ /* Handle any final pixel (including the special case where npixels == 1). */
+
+ if (aend != end)
+ {
+ /* The leftmost column is contained in source bits 7:4 and in
+ * destination bits 7:4
+ */
+
+ buffer[aend - start] = run[aend] & 0xf0;
+ }
+ }
+ else
+ {
+ uint8_t curr = run[start];
+ uint8_t last;
+
+ /* Now construct the rest of the bytes in the run (possibly special
+ * casing the final, partial byte below).
+ */
+
+ for (i = start + 1; i < aend; i++)
+ {
+ /* bits 3:0 from previous byte to run bits 7:4;
+ * bits 7:4 of current byte to run bits 3:0
+ */
+
+ last = curr;
+ curr = run[i];
+ *buffer++ = (last << 4) | (curr >> 4);
+ }
+
+ /* Handle any final pixel (including the special case where npixels == 1). */
+
+ if (aend != end)
+ {
+ /* The leftmost column is contained in source bits 3:0 and in
+ * destination bits 7:4
+ */
+
+ *buffer = (curr << 4);
+ }
+ }
+
+ return OK;
+}
+#else
+static int rit_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels)
+{
+ /* Can't read from OLED GDDRAM in SPI mode */
+
+ return -ENOSYS;
+}
+#endif
+
+/**************************************************************************************
+ * Name: rit_getvideoinfo
+ *
+ * Description:
+ * Get information about the LCD video controller configuration.
+ *
+ **************************************************************************************/
+
+static int rit_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo)
+{
+ DEBUGASSERT(dev && vinfo);
+ gvdbg("fmt: %d xres: %d yres: %d nplanes: %d\n",
+ g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres, g_videoinfo.nplanes);
+ memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: rit_getplaneinfo
+ *
+ * Description:
+ * Get information about the configuration of each LCD color plane.
+ *
+ **************************************************************************************/
+
+static int rit_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo)
+{
+ DEBUGASSERT(pinfo && planeno == 0);
+ gvdbg("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+ memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: rit_getpower
+ *
+ * Description:
+ * Get the LCD panel power status (0: full off - CONFIG_LCD_MAXPOWER: full on. On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int rit_getpower(FAR struct lcd_dev_s *dev)
+{
+ FAR struct rit_dev_s *priv = (FAR struct rit_dev_s *)dev;
+ DEBUGASSERT(priv);
+
+ gvdbg("power: %s\n", priv->on ? "ON" : "OFF");
+ return priv->on ? CONFIG_LCD_MAXPOWER : 0;
+}
+
+/**************************************************************************************
+ * Name: rit_setpower
+ *
+ * Description:
+ * Enable/disable LCD panel power (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int rit_setpower(struct lcd_dev_s *dev, int power)
+{
+ struct rit_dev_s *priv = (struct rit_dev_s *)dev;
+ DEBUGASSERT(priv && (unsigned)power <= CONFIG_LCD_MAXPOWER && priv->spi);
+
+ gvdbg("power: %d\n", power);
+
+ /* Select the SD1329 controller */
+
+ rit_select(priv->spi);
+
+ /* Only two power settings -- 0: sleep on, 1: sleep off */
+
+ if (power > 0)
+ {
+ /* Re-initialize the SSD1329 controller */
+
+ rit_sndcmds(priv, g_initcmds);
+
+ /* Take the display out of sleep mode */
+
+ rit_sndcmd(priv, g_sleepoff, sizeof(g_sleepoff));
+ priv->on = true;
+ }
+ else
+ {
+ /* Put the display into sleep mode */
+
+ rit_sndcmd(priv, g_sleepon, sizeof(g_sleepon));
+ priv->on = false;
+ }
+
+ /* De-select the SD1329 controller */
+
+ rit_deselect(priv->spi);
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: rit_getcontrast
+ *
+ * Description:
+ * Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int rit_getcontrast(struct lcd_dev_s *dev)
+{
+ struct rit_dev_s *priv = (struct rit_dev_s *)dev;
+
+ gvdbg("contrast: %d\n", priv->contrast);
+ return priv->contrast;
+}
+
+/**************************************************************************************
+ * Name: rit_setcontrast
+ *
+ * Description:
+ * Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int rit_setcontrast(struct lcd_dev_s *dev, unsigned int contrast)
+{
+ struct rit_dev_s *priv = (struct rit_dev_s *)dev;
+ uint8_t cmd[3];
+
+ gvdbg("contrast: %d\n", contrast);
+ DEBUGASSERT(contrast <= CONFIG_LCD_MAXCONTRAST);
+
+ /* Select the SD1329 controller */
+
+ rit_select(priv->spi);
+
+ /* Set new contrast */
+
+ cmd[0] = SSD1329_SET_CONTRAST;
+ cmd[1] = contrast;
+ cmd[2] = SSD1329_NOOP;
+ rit_sndcmd(priv, cmd, 3);
+
+ /* De-select the SD1329 controller */
+
+ rit_deselect(priv->spi);
+ priv->contrast = contrast;
+ return OK;
+}
+
+/**************************************************************************************
+ * Public Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: rit_initialize
+ *
+ * Description:
+ * Initialize the P14201 video hardware. The initial state of the OLED is fully
+ * initialized, display memory cleared, and the OLED ready to use, but with the power
+ * setting at 0 (full off == sleep mode).
+ *
+ * Input Parameters:
+ * spi - A reference to the SPI driver instance.
+ * devno - A value in the range of 0 throuh CONFIG_P14201_NINTERFACES-1. This allows
+ * support for multiple OLED devices.
+ *
+ * Returned Value:
+ * On success, this function returns a reference to the LCD object for the specified
+ * OLED. NULL is returned on any failure.
+ *
+ **************************************************************************************/
+
+FAR struct lcd_dev_s *rit_initialize(FAR struct spi_dev_s *spi, unsigned int devno)
+{
+ FAR struct rit_dev_s *priv = (FAR struct rit_dev_s *)&g_oleddev;
+ DEBUGASSERT(devno == 0 && spi);
+
+ gvdbg("Initializing devno: %d\n", devno);
+
+ /* Driver state data */
+
+ priv->spi = spi;
+ priv->contrast = RIT_CONTRAST;
+ priv->on = false;
+
+ /* Select the SD1329 controller */
+
+ rit_configspi(spi);
+ rit_select(spi);
+
+ /* Clear the display */
+
+ rit_clear(priv);
+
+ /* Configure (but don't enable) the OLED */
+
+ rit_sndcmds(priv, g_initcmds);
+
+ /* De-select the SD1329 controller */
+
+ rit_deselect(spi);
+ return &priv->dev;
+}
+#endif /* CONFIG_LCD_P14201 */
diff --git a/nuttx/drivers/lcd/pcf8833.h b/nuttx/drivers/lcd/pcf8833.h
new file mode 100644
index 000000000..36dc65ac3
--- /dev/null
+++ b/nuttx/drivers/lcd/pcf8833.h
@@ -0,0 +1,152 @@
+/**************************************************************************************
+ * drivers/lcd/pcf8833.h
+ * Definitions for the Phillips PCF8833 LCD controller
+ *
+ * Copyright (C) 2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: "Data Sheet, PCF8833 STN RGB 132x132x3 driver," Phillips, 2003 Feb 14.
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ **************************************************************************************/
+
+#ifndef __DRIVERS_LCD_PCF8833_H
+#define __DRIVERS_LCD_PCF8833_H
+
+/**************************************************************************************
+ * Included Files
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Pre-processor Definitions
+ **************************************************************************************/
+/* Pixel format codes */
+
+#define PCF8833_FMT_8BPS (2)
+#define PCF8833_FMT_12BPS (3)
+#define PCF8833_FMT_16BPS (5)
+
+/* LCD Commands */
+
+#define PCF8833_NOP 0x00 /* No operation; Data: none */
+#define PCF8833_SWRESET 0x01 /* Software reset ; Data: none */
+#define PCF8833_BSTROFF 0x02 /* Booster voltage off; Data: none */
+#define PCF8833_BSTRON 0x03 /* Booster voltage on; Data: none */
+#define PCF8833_RDDIDIF 0x04 /* Read display identification; Data: none */
+#define PCF8833_RDDST 0x09 /* Read display status; Data: none */
+#define PCF8833_SLEEPIN 0x10 /* Sleep_IN; Data: none */
+#define PCF8833_SLEEPOUT 0x11 /* Sleep_OUT; Data: none */
+#define PCF8833_PTLON 0x12 /* Partial mode on; Data: none */
+#define PCF8833_NORON 0x13 /* Normal Display mode on; Data: none */
+#define PCF8833_INVOFF 0x20 /* Display inversion off; Data: none */
+#define PCF8833_INVON 0x21 /* Display inversion on; Data: none */
+#define PCF8833_DALO 0x22 /* All pixel off; Data: none */
+#define PCF8833_DAL 0x23 /* All pixel on; Data: none */
+#define PCF8833_SETCON 0x25 /* Set contrast; Data: (1) contrast */
+#define PCF8833_DISPOFF 0x28 /* Display off; Data: none */
+#define PCF8833_DISPON 0x29 /* Display on; Data: none */
+#define PCF8833_CASET 0x2a /* Column address set; Data: (1) X start (2) X end */
+#define PCF8833_PASET 0x2b /* Page address set Data: (1) Y start (2) Y end */
+#define PCF8833_RAMWR 0x2c /* Memory write; Data: (1) write data */
+#define PCF8833_RGBSET 0x2d /* Colour set; Data: (1-8) red tones, (9-16) green tones, (17-20) blue tones */
+#define PCF8833_PTLAR 0x30 /* Partial area; Data: (1) start address (2) end address */
+#define PCF8833_VSCRDEF 0x33 /* Vertical scroll definition; Data: (1) top fixed, (2) scrol area, (3) bottom fixed */
+#define PCF8833_TEOFF 0x34 /* Tearing line off; Data: none */
+#define PCF8833_TEON 0x35 /* Tearing line on; Data: (1) don't care */
+#define PCF8833_MADCTL 0x36 /* Memory data access control; Data: (1) access control settings */
+#define PCF8833_SEP 0x37 /* Set Scroll Entry Point; Data: (1) scroll entry point */
+#define PCF8833_IDMOFF 0x38 /* Idle mode off; Data: none */
+#define PCF8833_IDMON 0x39 /* Idle mode on; Data: none */
+#define PCF8833_COLMOD 0x3a /* Interface pixel format; Data: (1) color interface format */
+#define PCF8833_SETVOP 0xb0 /* Set VOP; Data: (1) VOP5-8 (2) VOP0-4 */
+#define PCF8833_BRS 0xb4 /* Bottom Row Swap; Data: none */
+#define PCF8833_TRS 0xb6 /* Top Row Swap; Data: none */
+#define PCF8833_FINV 0xb9 /* Super Frame INVersion; Data: none */
+#define PCF8833_DOR 0xba /* Data ORder; Data: none */
+#define PCF8833_TCDFE 0xbd /* Enable/disable DF temp comp; Data: none */
+#define PCF8833_TCVOPE 0xbf /* Enable or disable VOP temp comp; Data: none */
+#define PCF8833_EC 0xc0 /* Internal or external oscillator; Data: none */
+#define PCF8833_SETMUL 0xc2 /* Set multiplication factor; Data: (1) Multiplication factor */
+#define PCF8833_TCVOPAB 0xc3 /* Set TCVOP slopes A and B; Data: (1) SLB and SLA */
+#define PCF8833_TCVOPCD 0xc4 /* Set TCVOP slopes C and D; Data: (1) SLD and SLC */
+#define PCF8833_TCDF 0xc5 /* Set divider frequency; Data: Divider factor in region (1) A (2) B (3) C (4) D */
+#define PCF8833_DF8COLOR 0xc6 /* Set divider frequency 8-colour mode; Data: (1) DF80-6 */
+#define PCF8833_SETBS 0xc7 /* Set bias system; Data: (1) Bias systems */
+#define PCF8833_RDTEMP 0xc8 /* Temperature read back; Data: none */
+#define PCF8833_NLI 0xc9 /* N-Line Inversion; Data: (1) NLI time slots invervsion */
+#define PCF8833_RDID1 0xda /* Read ID1; Data: none */
+#define PCF8833_RDID2 0xdb /* Read ID2; Data: none */
+#define PCF8833_RDID3 0xdc /* Read ID3; Data: none */
+#define PCF8833_SFD 0xef /* Select factory defaults; Data: none */
+#define PCF8833_ECM 0xf0 /* Enter Calibration mode; Data: (1) Calibration control settings */
+#define PCF8833_OTPSHTIN 0xf1 /* Shift data in OTP shift registers; Data: Any number of bytes */
+
+/* Memory data access control (MADCTL) bit definitions */
+
+#define MADCTL_RGB (1 << 3) /* Bit 3: BGR */
+#define MADCTL_LAO (1 << 4) /* Bit 4: Line address order bottom to top */
+#define MADCTL_V (1 << 5) /* Bit 5: Vertical RAM write; in Y direction */
+#define MADCTL_MX (1 << 6) /* Bit 6: Mirror X */
+#define MADCTL_MY (1 << 7) /* Bit 7: Mirror Y */
+
+/* PCF8833 status register bit definitions */
+/* CMD format: RDDST command followed by four status bytes: */
+/* Byte 1: D31 d30 D29 D28 D27 D26 --- --- */
+
+#define PCF8833_ST_RGB (1 << 2) /* Bit 2: D26 - RGB/BGR order */
+#define PCF8833_ST_LINEADDR (1 << 3) /* Bit 3: D27 - Line address order */
+#define PCF8833_ST_ADDRMODE (1 << 4) /* Bit 4: D28 - Vertical/horizontal addressing mode */
+#define PCF8833_ST_XADDR (1 << 5) /* Bit 5: D29 - X address order */
+#define PCF8833_ST_YADDR (1 << 6) /* Bit 6: D30 - Y address order */
+#define PCF8833_ST_BOOSTER (1 << 7) /* Bit 7: D31 - Booster voltage status */
+
+/* Byte 2: --- D22 D21 D20 D19 D18 D17 D16 */
+
+#define PCF8833_ST_NORMAL (1 << 0) /* Bit 0: D16 - Normal display mode */
+#define PCF8833_ST_SLEEPIN (1 << 1) /* Bit 1: D17 - Sleep in selected */
+#define PCF8833_ST_PARTIAL (1 << 2) /* Bit 2: D18 - Partial mode on */
+#define PCF8833_ST_IDLE (1 << 3) /* Bit 3: D19 - Idle mode selected */
+#define PCF8833_ST_PIXELFMT_SHIFT (4) /* Bits 4-6: D20-D22 - Interface pixel format */
+#define PCF8833_ST_PIXELFMT_MASK (7 << PCF8833_ST_PIXELFMT_SHIFT)
+# define PCF8833_ST_PIXELFMT_8BPS (PCF8833_FMT_8BPS << PCF8833_ST_PIXELFMT_SHIFT)
+# define PCF8833_ST_PIXELFMT_12BPS (PCF8833_FMT_12BPS << PCF8833_ST_PIXELFMT_SHIFT)
+# define PCF8833_ST_PIXELFMT_16BPS (PCF8833_FMT_16BPS << PCF8833_ST_PIXELFMT_SHIFT)
+
+/* Byte 3: D15 -- D13 D12 D11 D10 D9 --- */
+
+#define PCF8833_ST_TEARING (1 << 1) /* Bit 1: D9 - Tearing effect on */
+#define PCF8833_ST_DISPLAYON (1 << 2) /* Bit 2: D10 - Display on */
+#define PCF8833_ST_PIXELSOFF (1 << 3) /* Bit 3: D11 - All pixels off */
+#define PCF8833_ST_PIXELSON (1 << 4) /* Bit 4: D12 - All pixels on */
+#define PCF8833_ST_INV (1 << 5) /* Bit 5: D13 - Display inversion */
+#define PCF8833_ST_VSCROLL (1 << 7) /* Bit 6: D15 - Vertical scroll mode */
+
+/* Byte 4: All zero */
+
+#endif /* __DRIVERS_LCD_PCF8833_H */ \ No newline at end of file
diff --git a/nuttx/drivers/lcd/s1d15g10.h b/nuttx/drivers/lcd/s1d15g10.h
new file mode 100644
index 000000000..9b5f7738f
--- /dev/null
+++ b/nuttx/drivers/lcd/s1d15g10.h
@@ -0,0 +1,141 @@
+/**************************************************************************************
+ * drivers/lcd/s1d15g10.h
+ * Definitions for the Epson S1D15G0 LCD controller
+ *
+ * Copyright (C) 2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: S1D15G0D08B000, Seiko Epson Corportation, 2002.
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ **************************************************************************************/
+
+#ifndef __DRIVERS_LCD_S1D15G10_H
+#define __DRIVERS_LCD_S1D15G10_H
+
+/**************************************************************************************
+ * Included Files
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Pre-processor Definitions
+ **************************************************************************************/
+
+/* Epson S1D15G10 Command Set */
+
+#define S1D15G10_DISON 0xaf /* Display on; Data: none */
+#define S1D15G10_DISOFF 0xae /* Display off; Data: none */
+#define S1D15G10_DISNOR 0xa6 /* Normal display; Data: none */
+#define S1D15G10_DISINV 0xa7 /* Inverse display; Data: none */
+#define S1D15G10_COMSCN 0xbb /* Common scan direction; Data: (1) common scan direction */
+#define S1D15G10_DISCTL 0xca /* Display control; Data: Data: (1) CL div, F1/2 pat, (2) duty, (3) FR inverse (4) dispersion */
+#define S1D15G10_SLPIN 0x95 /* Sleep in; Data: none */
+#define S1D15G10_SLPOUT 0x94 /* Sleep out; Data: none */
+#define S1D15G10_PASET 0x75 /* Page address set; Data: (1) start page, (2) end page */
+#define S1D15G10_CASET 0x15 /* Column address set; Data: (1) start addr, (2) end addr */
+#define S1D15G10_DATCTL 0xbc /* Data scan direction, etc.; Data: (1) inverse, scan dir (2) RGB, (3) gray-scale */
+#define S1D15G10_RGBSET8 0xce /* 256-color position set; Data: (1-8) red tones, (9-16) green tones, (17-20) blue tones */
+#define S1D15G10_RAMWR 0x5c /* Writing to memory; Data: (1) write data */
+#define S1D15G10_RAMRD 0x5d /* Reading from memory; Data: (1) read data */
+#define S1D15G10_PTLIN 0xa8 /* Partial display in; Data: (1) start addr, (2) end addr */
+#define S1D15G10_PTLOUT 0xa9 /* Partial display out; Data: none */
+#define S1D15G10_RMWIN 0xe0 /* Read and modify write; Data: none */
+#define S1D15G10_RMWOUT 0xee /* End; Data: none */
+#define S1D15G10_ASCSET 0xaa /* Area scroll set; Data: (1) top addr, (2) bottom addr, (3) Num blocks, (4) scroll mode */
+#define S1D15G10_SCSTART 0xab /* Scroll start set; Data: (1) start block addr */
+#define S1D15G10_OSCON 0xd1 /* Internal oscillation on; Data: none */
+#define S1D15G10_OSCOFF 0xd2 /* Internal oscillation off; Data: none */
+#define S1D15G10_PWRCTR 0x20 /* Power control; Data: (1) LCD drive power */
+#define S1D15G10_VOLCTR 0x81 /* Electronic volume control; Data: (1) volume value, (2) resistance ratio */
+#define S1D15G10_VOLUP 0xd6 /* Increment electronic control by 1; Data: none */
+#define S1D15G10_VOLDOWN 0xd7 /* Decrement electronic control by 1; Data: none */
+#define S1D15G10_TMPGRD 0x82 /* Temperature gradient set; Data: (1-14) temperature gradient */
+#define S1D15G10_EPCTIN 0xcd /* Control EEPROM; Data: (1) read/write */
+#define S1D15G10_EPCOUT 0xcc /* Cancel EEPROM control; Data: none */
+#define S1D15G10_EPMWR 0xfc /* Write into EEPROM; Data: none */
+#define S1D15G10_EPMRD 0xfd /* Read from EEPROM; Data: none */
+#define S1D15G10_EPSRRD1 0x7c /* Read register 1; Data: none */
+#define S1D15G10_EPSRRD2 0x7d /* Read regiser 2; Data: none */
+#define S1D15G10_NOP 0x25 /* NOP intruction (0x45?); Data: none */
+#define S1D15G10_STREAD 0x20 /* Status read; Data: none */
+
+/* Display control (DISCTL) bit definitions */
+
+#define DISCTL_PERIOD_SHIFT (0) /* P1: Bits 0-1, F1 and F2 drive-pattern switching period */
+#define DISCTL_PERIOD_MASK (3 << DISCTL_PERIOD_SHIFT)
+# define DISCTL_PERIOD_8 (0 << DISCTL_PERIOD_SHIFT)
+# define DISCTL_PERIOD_4 (1 << DISCTL_PERIOD_SHIFT)
+# define DISCTL_PERIOD_16 (2 << DISCTL_PERIOD_SHIFT)
+# define DISCTL_PERIOD_FLD (3 << DISCTL_PERIOD_SHIFT)
+#define DISCTL_CLDIV_SHIFT (2) /* P1: Bits 2-4, Clock divider */
+#define DISCTL_CLDIV_MASK (7 << DISCTL_CLDIV_SHIFT)
+# define DISCTL_CLDIV_2 (0 << DISCTL_CLDIV_SHIFT)
+# define DISCTL_CLDIV_4 (1 << DISCTL_CLDIV_SHIFT)
+# define DISCTL_CLDIV_8 (2 << DISCTL_CLDIV_SHIFT)
+# define DISCTL_CLDIV_NONE (3 << DISCTL_CLDIV_SHIFT)
+
+/* Power control (PWRCTR) bit definitions */
+
+#define PWCTR_REFVOLTAGE (1 << 0) /* P1: Bit 0, Turn on reference voltage generation circuit. */
+#define PWCTR_REGULATOR (1 << 1) /* P1: Bit 1, Turn on voltage regulator and circuit voltage follower. */
+#define PWCTR_BOOSTER2 (1 << 2) /* P1: Bit 2, Turn on secondary booster/step-down circuit. */
+#define PWCTR_BOOSTER1 (1 << 3) /* P1: Bit 3, Turn on primary booster circuit. */
+#define PWCTR_EXTR (1 << 4) /* P1: Bit 4, Use external resistance to adjust voltage. */
+
+/* Data control (DATCTL) bit definitions */
+
+#define DATCTL_PGADDR_INV (1 << 0) /* P1: Bit 0, Inverse display of the page address. */
+#define DATCTL_COLADDR_REV (1 << 1) /* P1: Bit 1, Reverse turn of column address. */
+#define DATCTL_ADDR_PGDIR (1 << 2) /* P1: Bit 2, Address-scan direction in page (vs column) direction. */
+
+#define DATCTL_BGR (1 << 0) /* P2: Bit0, RGB->BGR */
+
+#define DATCTL_8GRAY (1) /* P3: Bits 0-2 = 001, 8 gray-scale */
+#define DATCTL_16GRAY_A (2) /* P3: Bits 0-2 = 010, 16 gray-scale display type A */
+#define DATCTL_16GRAY_B (4) /* P3: Bits 0-2 = 100, 16 gray-scale display type B */
+
+/* Status register bit definions (after reset or NOP) */
+
+#define S1D15G10_SR_PARTIAL (1 << 0) /* Bit 0: Partial display */
+#define S1D15G10_SR_NORMAL (1 << 1) /* Bit 1: Normal (vs. inverse) display */
+#define S1D15G10_SR_EEPROM (1 << 2) /* Bit 2: EEPROM access */
+#define S1D15G10_SR_DISPON (1 << 3) /* Bit 3: Display on */
+#define S1D15G10_SR_COLSCAN (1 << 4) /* Bit 4: Column (vs. page) scan direction */
+#define S1D15G10_SR_RMW (1 << 5) /* Bit 5: Read modify write */
+#define S1D15G10_SR_SCROLL (3 << 6) /* Bits 6-7: Area scroll mode */
+
+/* Status register bit definions (after EPSRRD1) */
+
+#define S1D15G10_SR_VOLUME 0x3f /* Bits 0-5: Electronic volume control values */
+
+/* Status register bit definions (after EPSRRD2) */
+
+#define S1D15G10_SR_RRATIO 0x07 /* Bits 0-2: Built-in resistance ratio */
+
+#endif /* __DRIVERS_LCD_S1D15G10_H */ \ No newline at end of file
diff --git a/nuttx/drivers/lcd/sd1329.h b/nuttx/drivers/lcd/sd1329.h
new file mode 100644
index 000000000..5d2ad4948
--- /dev/null
+++ b/nuttx/drivers/lcd/sd1329.h
@@ -0,0 +1,527 @@
+/****************************************************************************
+ * drivers/lcd/sd1329.h
+ *
+ * Copyright (C) 2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_LCD_SD1329_H
+#define __DRIVERS_LCD_SD1329_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+/* SD1329 Commands **********************************************************/
+/* Set column Address.
+ *
+ * This triple byte command specifies column start address and end address of
+ * the display data RAM. This command also sets the column address pointer to
+ * column start address. This pointer is used to define the current read/write
+ * column address in graphic display data RAM. If horizontal address increment
+ * mode is enabled by command 0xa0, after finishing read/write one column data,
+ * it is incremented automatically to the next column address. Whenever the
+ * column address pointer finishes accessing the end column address, it is
+ * reset back to start column address and the row address is incremented to the
+ * next row.
+ *
+ * Byte 1: 0x15
+ * Byte 2: A[5:0]: Start Address, range: 0x00-0x3f
+ * Byte 3: B[5:0]: End Address, range: 0x00-0x3f
+ */
+
+#define SSD1329_SET_COLADDR 0x15
+
+/* Set Row Address.
+ *
+ * This triple byte command specifies row start address and end address of the
+ * display data RAM. This command also sets the row address pointer to row
+ * start address. This pointer is used to define the current read/write row
+ * address in graphic display data RAM. If vertical address increment mode is
+ * enabled by command 0xa0, after finishing read/write one row data, it is
+ * incremented automatically to the next row address. Whenever the row address
+ * pointer finishes accessing the end row address, it is reset back to start
+ * row address.
+ *
+ * Byte 1: 0x75
+ * Byte 2: A[6:0]: Start Address, range: 0x00-0x7f
+ * Byte 3: B[6:0]: End Address, range: 0x00-0x7f
+ */
+
+#define SSD1329_SET_ROWADDR 0x75
+
+/* Set Contract Current
+ *
+ * This double byte command is to set Contrast Setting of the display. The
+ * chip has 256 contrast steps from 0x00 to 0xff. The segment output current
+ * increases linearly with the increase of contrast step.
+ *
+ * Byte 1: 0x81
+ * Byte 2: A[7:0]: Contrast Value, range: 0-255
+ */
+
+#define SSD1329_SET_CONTRAST 0x81
+
+/* Set Second Pre-Charge Speed
+ *
+ * This command is used to set the speed of second pre-charge in phase 3.
+ * This speed can be doubled to achieve faster pre-charging through setting
+ * 0x82 A[0].
+ *
+ * Byte 1: 0x82
+ * Byte 2: A[7:1]: Second Pre-charge Speed
+ * A[0] = 1, Enable doubling the Second Pre-charge speed
+ */
+
+#define SSD1329_PRECHRG2_SPEED 0x82
+# define SSD1329_PRECHRG2_DBL 0x01
+
+/* Set Master Icon Control
+ *
+ * This double command is used to set the ON / OFF conditions of internal
+ * charge pump, icon circuits and overall icon status.
+ *
+ * Byte 1: 0x90
+ * Byte 2: Icon control (OR of bits 0-1,4-5)
+ */
+
+#define SSD1329_ICON_CONTROL 0x90
+# define SSD1329_ICON_NORMAL 0x00 /* A[1:0]1=00: Icon RESET to normal display */
+# define SSD1329_ICON_ALLON 0x01 /* A[1:0]1=01: Icon All ON */
+# define SSD1329_ICON_ALLOFF 0x02 /* A[1:0]=10: Icon All OFF */
+# define SSD1329_ICON_DISABLE 0x00 /* A[4]=0: Disable Icon display */
+# define SSD1329_ICON_ENABLE 0x10 /* A[4]=1: Enable Icon display */
+# define SSD1329_VICON_DISABLE 0x00 /* A[5]=0: Disable VICON charge pump circuit */
+# define SSD1329_VICON_ENABLE 0x20 /* A[5]=1: Enable VICON charge pump circuit */
+
+/* Set Icon Current Range
+ *
+ * This double byte command is used to set one fix current range for all icons
+ * between the range of 0uA and 127.5uA. The uniformity improves as the icon
+ * current range increases.
+ *
+ * Byte 1: 0x91
+ * Byte 2: A[7:0]: Max icon current:
+ * 00 = 0.0 uA
+ * 01 = 0.5 uA
+ * ...
+ * ff = 127.5 uA
+ */
+
+#define SSD1329_ICON_CURRRNG 0x91
+
+/* Set Individual Icon Current
+ *
+ * This multiple byte command is used to fine tune the current for each of the
+ * 64 icons. Command 0x92 followed by 64 single byte data. These 64 byte data
+ * have to be entered in order to make this command function. Below is the
+ * formula for calculating the icon current.
+ *
+ * Icon Current = Single byte value / 127 x Maximum icon current set with command 0x91
+ *
+ * Byte 1: 0x92
+ * Byte 2-65: An[6:0]: icon current for ICSn, range: 0x00-0x7f
+ * Icon Current of ICSn = An[6:0]/127) x max icon current
+ */
+
+#define SSD1329_ICON_CURRENT 0x92
+
+/* Set Individual Icon ON / OFF Register
+ *
+ * This double byte command is used to select one of the 64 icons and choose the
+ * ON, OFF or blinking condition of the selected icon.
+ *
+ * Byte 1: 0x93
+ * Byte 2: A[5:0]: Select one of the 64 icons from ICS0 ~ ICS63
+ * A[7:6]: OFF/ON/BLINK
+ */
+
+#define SSD1329_ICON_SELECT 0x93
+# define SSD1329_ICON_OFF 0x00
+# define SSD1329_ICON_ON 0x40
+# define SSD1329_ICON_BLINK 0xc0
+
+/* Set Icon ON / OFF Registers
+ *
+ * This double byte command is used to set the ON / OFF status of all 64 icons.
+ *
+ * Byte 1: 0x94
+ * Byte 2: A[7:6]: OFF/ON/BLINK (Same as 0x93)
+ */
+
+#define SSD1329_ICON_ALL 0x94
+
+/* Set Icon Blinking Cycle
+ *
+ * This double byte command is used to set icon oscillator frequency and
+ * blinking cycle selected with above command 0x93.
+ *
+ * Byte 1: 0x95
+ * Byte 2:
+ * - A[2:0]:Icon Blinking cycle
+ * - A[5:4]:Icon oscillation frequency
+ */
+
+#define SSD1329_ICON_BLINKING 0x95
+# define SSD1329_ICON_BLINK_0p25S 0x00 /* 0.25 sec */
+# define SSD1329_ICON_BLINK_0p50S 0x01 /* 0.50 sec */
+# define SSD1329_ICON_BLINK_0p75S 0x02 /* 0.75 sec */
+# define SSD1329_ICON_BLINK_0p100S 0x03 /* 1.00 sec */
+# define SSD1329_ICON_BLINK_0p125S 0x04 /* 1.25 sec */
+# define SSD1329_ICON_BLINK_0p150S 0x05 /* 1.50 sec */
+# define SSD1329_ICON_BLINK_0p175S 0x06 /* 1.75 sec */
+# define SSD1329_ICON_BLINK_0p200S 0x07 /* 2.00 sec */
+# define SSD1329_ICON_BLINK_61KHZ 0x00 /* 61 KHz */
+# define SSD1329_ICON_BLINK_64KHZ 0x10 /* 64 KHz */
+# define SSD1329_ICON_BLINK_68KHZ 0x20 /* 68 KHz */
+# define SSD1329_ICON_BLINK_73KHZ 0x30 /* 73 KHz */
+
+/* Set Icon Duty
+ *
+ * This double byte command is used to set the icon frame frequency and icon AC
+ * drive duty ratio.
+ *
+ * Byte 1: 0x96
+ * Byte 2:
+ * - A[2:0]: AC Drive
+ * - A[7:4]: con frame frequency
+ */
+
+#define SSD1329_ICON_ACDRIVE 0x96
+# define SSD1329_ICON_DUTY_DC 0x00
+# define SSD1329_ICON_DUTY_63_64 0x01
+# define SSD1329_ICON_DUTY_62_64 0x02
+# define SSD1329_ICON_DUTY_61_64 0x03
+# define SSD1329_ICON_DUTY_60_64 0x04
+# define SSD1329_ICON_DUTY_59_64 0x05
+# define SSD1329_ICON_DUTY_58_64 0x06
+# define SSD1329_ICON_DUTY_57_64 0x07
+
+/* Set Re-map
+ *
+ * This double command has multiple configurations and each bit setting is
+ * described as follows:
+ *
+ * Column Address Remapping (A[0])
+ * This bit is made for increase the flexibility layout of segment signals in
+ * OLED module with segment arranged from left to right (when A[0] is set to 0)
+ * or from right to left (when A[0] is set to 1).
+ *
+ * Nibble Remapping (A[1])
+ * When A[1] is set to 1, the two nibbles of the data bus for RAM access are
+ * re-mapped, such that (D7, D6, D5, D4, D3, D2, D1, D0) acts like (D3, D2, D1,
+ * D0, D7, D6, D5, D4) If this feature works together with Column Address
+ * Re-map, it would produce an effect of flipping the outputs from SEG0-127 to
+ * SEG127-SEG0.
+ *
+ * Address increment mode (A[2])
+ * When A[2] is set to 0, the driver is set as horizontal address incremen
+ * mode. After the display RAM is read/written, the column address pointer is
+ * increased automatically by 1. If the column address pointer reaches column
+ * end address, the column address pointer is reset to column start address and
+ * row address pointer is increased by 1.
+ *
+ * When A[2] is set to 1, the driver is set to vertical address increment mode.
+ * After the display RAM is read/written, the row address pointer is increased
+ * automatically by 1. If the row address pointer reaches the row end address,
+ * the row address pointer is reset to row start address and column address
+ * pointer is increased by 1.
+ *
+ * COM Remapping (A[4])
+ * This bit defines the scanning direction of the common for flexible layout
+ * of common signals in OLED module either from up to down (when A[4] is set to
+ * 0) or from bottom to up (when A[4] is set to 1).
+ *
+ * Splitting of Odd / Even COM Signals (A[6])
+ * This bit is made to match the COM layout connection on the panel. When A[6]
+ * is set to 0, no splitting odd / even of the COM signal is performed. When
+ * A[6] is set to 1, splitting odd / even of the COM signal is performed,
+ * output pin assignment sequence is shown as below (for 128MUX ratio):
+ *
+ * Byte 1: 0xa0
+ * Byte 2: A[7:0]
+ */
+
+#define SSD1329_GDDRAM_REMAP 0xa0
+# define SSD1329_COLADDR_REMAP 0x01 /* A[0]: Enable column re-map */
+# define SSD1329_NIBBLE_REMAP 0x02 /* A[1]: Enable nibble re-map */
+# define SSD1329_VADDR_INCR 0x04 /* A[1]: Enable vertical address increment */
+# define SSD1329_COM_REMAP 0x10 /* A[4]: Enable COM re-map */
+# define SSD1329_COM_SPLIT 0x40 /* A[6]: Enable COM slip even/odd */
+
+/* Set Display Start Line
+ *
+ * This double byte command is to set Display Start Line register for
+ * determining the starting address of display RAM to be displayed by selecting
+ * a value from 0 to 127.
+ *
+ * Byte 1: 0xa1
+ * Byte 2: A[6:0]: Vertical scroll by setting the starting address of
+ * display RAM from 0-127
+ */
+
+#define SSD1329_VERT_START 0xa1
+
+/* Set Display Offset
+ *
+ * This double byte command specifies the mapping of display start line (it is
+ * assumed that COM0 is the display start line, display start line register
+ * equals to 0) to one of COM0-COM127.
+ *
+ * Byte 1: 0xa2
+ * Byte 2: A[6:0]: Set vertical offset by COM from 0-127
+ */
+
+#define SSD1329_VERT_OFFSET 0xa2
+
+/* Set Display Mode - Normal, all on, all off, inverse
+ *
+ * These are single byte commands and are used to set display status to Normal
+ * Display, Entire Display ON, Entire Display OFF or Inverse Display.
+ *
+ * Normal Display (0xa4)
+ * Reset the “Entire Display ON, Entire Display OFF or Inverse Display” effects
+ * and turn the data to ON at the corresponding gray level.
+ *
+ * Set Entire Display ON (0xa5)
+ * Force the entire display to be at gray scale level GS15, regardless of the
+ * contents of the display data RAM.
+ *
+ * Set Entire Display OFF (0xa6)
+ * Force the entire display to be at gray scale level GS0, regardless of the
+ * contents of the display data RAM.
+ *
+ * Inverse Display (0xa7)
+ * The gray scale level of display data are swapped such that “GS0” <-> “GS15”,
+ * “GS1” <-> “GS14”, etc.
+ *
+ * Byte 1: Display mode command
+ */
+
+#define SSD1329_DISP_NORMAL 0xa4
+#define SSD1329_DISP_OFF 0xa5
+#define SSD1329_DISP_ON 0xa6
+#define SSD1329_DISP_INVERT 0xa7
+
+/* Set MUX Ratio
+ *
+ * This double byte command sets multiplex ratio (MUX ratio) from 16MUX to
+ * 128MUX. In POR, multiplex ratio is 128MUX.
+ *
+ * Byte 1: 0xa8
+ * Byte 2: A[6:0] 15-127 representing 16-128 MUX
+ */
+
+#define SSD1329_MUX_RATIO 0xa8
+
+/* Set Sleep mode ON / OFF
+ *
+ * These single byte commands are used to turn the matrix display on the OLED
+ * panel display either ON or OFF. When the sleep mode is set to ON (0xae), the
+ * display is OFF, the segment and common output are in high impedance state
+ * and circuits will be turned OFF. When the sleep mode is set to OFF (0xaf),
+ * the display is ON.
+ *
+ * Byte 1: sleep mode command
+ */
+
+#define SSD1329_SLEEP_ON 0xae
+#define SSD1329_SLEEP_OFF 0xaf
+
+/* Set Phase Length
+ *
+ * In the second byte of this double command, lower nibble and higher nibble is
+ * defined separately. The lower nibble adjusts the phase length of Reset (phase
+ * 1). The higher nibble is used to select the phase length of first pre-charge
+ * phase (phase 2). The phase length is ranged from 1 to 16 DCLK's. RESET for
+ * A[3:0] is set to 3 which means 4 DCLK’s selected for Reset phase. POR for
+ * A[7:4] is set to 5 which means 6 DCLK’s is selected for first pre-charge
+ * phase. Please refer to Table 9-1 for detail breakdown levels of each step.
+ *
+ * Byte 1: 0xb1
+ * Byte 2: A[3:0]: Phase 1 period of 1~16 DCLK’s
+ * A[7:4]: Phase 2 period of 1~16 DCLK’s
+ */
+
+#define SSD1329_PHASE_LENGTH 0xb1
+
+/* Set Frame Frequency
+ *
+ * This double byte command is used to set the number of DCLK’s per row between
+ * the range of 0x14 and 0x7f. Then the Frame frequency of the matrix display
+ * is equal to DCLK frequency / A[6:0].
+ *
+ * Byte 1: 0xb2
+ * Byte 2: A[6:0]:Total number of DCLK’s per row. Ranging from
+ * 0x14 to 0x4e DCLK’s. frame Frequency = DCLK freq /A[6:0].
+ */
+
+#define SSD1329_FRAME_FREQ 0xb2
+
+/* Set Front Clock Divider / Oscillator Frequency
+ *
+ * This double command is used to set the frequency of the internal display
+ * clocks, DCLK's. It is defined by dividing the oscillator frequency by the
+ * divide ratio (Value from 1 to 16). Frame frequency is determined by divide
+ * ratio, number of display clocks per row, MUX ratio and oscillator frequency.
+ * The lower nibble of the second byte is used to select the oscillator
+ * frequency. Please refer to Table 9-1 for detail breakdown levels of each
+ * step.
+ *
+ * Byte 1: 0xb3
+ * Byte 2: A[3:0]: Define divide ratio (D) of display clock (DCLK)
+ * Divide ratio=A[3:0]+1
+ * A[7:4] : Set the Oscillator Frequency, FOSC. Range:0-15
+ */
+
+#define SSD1329_DCLK_DIV 0xb3
+
+/* Set Default Gray Scale Table
+ *
+ * This single byte command is used to set the gray scale table to initial
+ * default setting.
+ *
+ * Byte 1: 0xb7
+ */
+
+#define SSD1329_GSCALE_TABLE 0xb7
+
+/* Look Up Table for Gray Scale Pulse width
+ *
+ * This command is used to set each individual gray scale level for the display.
+ * Except gray scale level GS0 that has no pre-charge and current drive, each
+ * gray scale level is programmed in the length of current drive stage pulse
+ * width with unit of DCLK. The longer the length of the pulse width, the
+ * brighter the OLED pixel when it’s turned ON.
+ *
+ * The setting of gray scale table entry can perform gamma correction on OLED
+ * panel display. Normally, it is desired that the brightness response of the
+ * panel is linearly proportional to the image data value in display data RAM.
+ * However, the OLED panel is somehow responded in non-linear way. Appropriate
+ * gray scale table setting like example below can compensate this effect.
+ *
+ * Byte 1: 0xb8
+ * Bytes 2-16: An[5:0], value for GSn level Pulse width
+ */
+
+#define SSD1329_GSCALE_LOOKUP 0xb8
+
+/* Set Second Pre-charge Period
+ *
+ * This double byte command is used to set the phase 3 second pre-charge period.
+ * The period of phase 3 can be programmed by command 0xbb and it is ranged from
+ * 0 to 15 DCLK's.
+ *
+ * Byte 1: 0xbb
+ * Byte 2: 0-15 DCLKs
+ */
+
+#define SSD1329_PRECHRG2_PERIOD 0xbb
+
+/* Set First Precharge voltage, VP
+ *
+ * This double byte command is used to set phase 2 first pre-charge voltage
+ * level. It can be programmed to set the first pre-charge voltage reference to
+ * VCC or VCOMH.
+ *
+ * Byte 1: 0xbc
+ * Byte 2: A[5] == 0, Pre-charge voltage is (0.30 + A[4:0]) * Vcc
+ * A{5] == 1, 1.00 x VCC or connect to VCOMH if VCC > VCOMH
+ */
+
+#define SSD1329_PRECHRG1_VOLT 0xbc
+
+/* Set VCOMH
+ *
+ * This double byte command sets the high voltage level of common pins, VCOMH.
+ * The level of VCOMH is programmed with reference to VCC.
+ *
+ * Byte 1: 0xbe
+ * Byte 2: (0.51 + A[5:0]) * Vcc
+ */
+
+#define SSD1329_COM_HIGH 0xbe
+
+/* NOOP
+ *
+ * This is a no operation command.
+ *
+ * Byte 1: 0xe3
+ */
+
+#define SSD1329_NOOP 0xe3
+
+/* Set Command Lock
+ *
+ * This command is used to lock the MCU from accepting any command.
+ *
+ * Byte 1: 0xfd
+ * Byte 2: 0x12 | A[2]
+ * A[2] == 1, Enable locking the MCU from entering command
+ */
+
+#define SSD1329_CMD_LOCK 0xfd
+# define SSD1329_LOCK_ON 0x13
+# define SSD1329_LOCK_OFF 0x12
+
+/* SD1329 Status ************************************************************/
+
+#define SDD1329_STATUS_ON 0x00 /* D[6]=0: indicates the display is ON */
+#define SDD1329_STATUS_OFF 0x40 /* D[6]=1: indicates the display is OFF */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+#endif /* __DRIVERS_LCD_SD1329_H */
diff --git a/nuttx/drivers/lcd/skeleton.c b/nuttx/drivers/lcd/skeleton.c
new file mode 100644
index 000000000..83aa92018
--- /dev/null
+++ b/nuttx/drivers/lcd/skeleton.c
@@ -0,0 +1,401 @@
+/**************************************************************************************
+ * drivers/lcd/skeleton.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Included Files
+ **************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi.h>
+#include <nuttx/lcd/lcd.h>
+
+#include "up_arch.h"
+
+/**************************************************************************************
+ * Pre-processor Definitions
+ **************************************************************************************/
+
+/* Configuration **********************************************************************/
+/* Verify that all configuration requirements have been met */
+
+/* Debug ******************************************************************************/
+/* Define the following to enable register-level debug output */
+
+#undef CONFIG_LCD_SKELDEBUG
+
+/* Verbose debug must also be enabled */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_DEBUG_GRAPHICS
+#endif
+
+#ifndef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_LCD_SKELDEBUG
+#endif
+
+/* Color Properties *******************************************************************/
+
+/* Display Resolution */
+
+#define SKEL_XRES 320
+#define SKEL_YRES 240
+
+/* Color depth and format */
+
+#define SKEL_BPP 16
+#define SKEL_COLORFMT FB_FMT_RGB16_565
+
+/* Debug ******************************************************************************/
+
+#ifdef CONFIG_LCD_SKELDEBUG
+# define skeldbg(format, arg...) vdbg(format, ##arg)
+#else
+# define skeldbg(x...)
+#endif
+
+/**************************************************************************************
+ * Private Type Definition
+ **************************************************************************************/
+
+/* This structure describes the state of this driver */
+
+struct skel_dev_s
+{
+ /* Publically visible device structure */
+
+ struct lcd_dev_s dev;
+
+ /* Private LCD-specific information follows */
+};
+
+/**************************************************************************************
+ * Private Function Protototypes
+ **************************************************************************************/
+
+/* LCD Data Transfer Methods */
+
+static int skel_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels);
+static int skel_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels);
+
+/* LCD Configuration */
+
+static int skel_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo);
+static int skel_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+# error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+# error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int skel_getpower(struct lcd_dev_s *dev);
+static int skel_setpower(struct lcd_dev_s *dev, int power);
+static int skel_getcontrast(struct lcd_dev_s *dev);
+static int skel_setcontrast(struct lcd_dev_s *dev, unsigned int contrast);
+
+/**************************************************************************************
+ * Private Data
+ **************************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane. This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8). Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently. However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint16_t g_runbuffer[SKEL_XRES];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+ .fmt = SKEL_COLORFMT, /* Color format: RGB16-565: RRRR RGGG GGGB BBBB */
+ .xres = SKEL_XRES, /* Horizontal resolution in pixel columns */
+ .yres = SKEL_YRES, /* Vertical resolution in pixel rows */
+ .nplanes = 1, /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+ .putrun = skel_putrun, /* Put a run into LCD memory */
+ .getrun = skel_getrun, /* Get a run from LCD memory */
+ .buffer = (uint8_t*)g_runbuffer, /* Run scratch buffer */
+ .bpp = SKEL_BPP, /* Bits-per-pixel */
+};
+
+/* This is the standard, NuttX LCD driver object */
+
+static struct skel_dev_s g_lcddev =
+{
+ .dev =
+ {
+ /* LCD Configuration */
+
+ .getvideoinfo = skel_getvideoinfo,
+ .getplaneinfo = skel_getplaneinfo,
+
+ /* LCD RGB Mapping -- Not supported */
+ /* Cursor Controls -- Not supported */
+
+ /* LCD Specific Controls */
+
+ .getpower = skel_getpower,
+ .setpower = skel_setpower,
+ .getcontrast = skel_getcontrast,
+ .setcontrast = skel_setcontrast,
+ },
+};
+
+/**************************************************************************************
+ * Private Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: skel_putrun
+ *
+ * Description:
+ * This method can be used to write a partial raster line to the LCD:
+ *
+ * row - Starting row to write to (range: 0 <= row < yres)
+ * col - Starting column to write to (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer containing the run to be written to the LCD
+ * npixels - The number of pixels to write to the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+static int skel_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels)
+{
+ /* Buffer must be provided and aligned to a 16-bit address boundary */
+
+ gvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);
+
+ /* Set up to write the run. */
+
+ /* Write the run to GRAM. */
+#warning "Missing logic"
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: skel_getrun
+ *
+ * Description:
+ * This method can be used to read a partial raster line from the LCD:
+ *
+ * row - Starting row to read from (range: 0 <= row < yres)
+ * col - Starting column to read read (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer in which to return the run read from the LCD
+ * npixels - The number of pixels to read from the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+static int skel_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels)
+{
+ /* Buffer must be provided and aligned to a 16-bit address boundary */
+
+ gvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);
+
+#warning "Missing logic"
+ return -ENOSYS;
+}
+
+/**************************************************************************************
+ * Name: skel_getvideoinfo
+ *
+ * Description:
+ * Get information about the LCD video controller configuration.
+ *
+ **************************************************************************************/
+
+static int skel_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo)
+{
+ DEBUGASSERT(dev && vinfo);
+ gvdbg("fmt: %d xres: %d yres: %d nplanes: %d\n",
+ g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres, g_videoinfo.nplanes);
+ memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: skel_getplaneinfo
+ *
+ * Description:
+ * Get information about the configuration of each LCD color plane.
+ *
+ **************************************************************************************/
+
+static int skel_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo)
+{
+ DEBUGASSERT(dev && pinfo && planeno == 0);
+ gvdbg("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+ memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: skel_getpower
+ *
+ * Description:
+ * Get the LCD panel power status (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int skel_getpower(struct lcd_dev_s *dev)
+{
+ struct skel_dev_s *priv = (struct skel_dev_s *)dev;
+ gvdbg("power: %d\n", 0);
+#warning "Missing logic"
+ return 0;
+}
+
+/**************************************************************************************
+ * Name: skel_setpower
+ *
+ * Description:
+ * Enable/disable LCD panel power (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int skel_setpower(struct lcd_dev_s *dev, int power)
+{
+ struct skel_dev_s *priv = (struct skel_dev_s *)dev;
+
+ gvdbg("power: %d\n", power);
+ DEBUGASSERT(power <= CONFIG_LCD_MAXPOWER);
+
+ /* Set new power level */
+#warning "Missing logic"
+
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: skel_getcontrast
+ *
+ * Description:
+ * Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int skel_getcontrast(struct lcd_dev_s *dev)
+{
+ gvdbg("Not implemented\n");
+#warning "Missing logic"
+ return -ENOSYS;
+}
+
+/**************************************************************************************
+ * Name: skel_setcontrast
+ *
+ * Description:
+ * Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int skel_setcontrast(struct lcd_dev_s *dev, unsigned int contrast)
+{
+ gvdbg("contrast: %d\n", contrast);
+#warning "Missing logic"
+ return -ENOSYS;
+}
+
+/**************************************************************************************
+ * Public Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: up_oledinitialize
+ *
+ * Description:
+ * Initialize the LCD video hardware. The initial state of the LCD is fully
+ * initialized, display memory cleared, and the LCD ready to use, but with the power
+ * setting at 0 (full off).
+ *
+ **************************************************************************************/
+
+FAR struct lcd_dev_s *up_oledinitialize(FAR struct spi_dev_s *spi)
+{
+ gvdbg("Initializing\n");
+
+ /* Configure GPIO pins */
+#warning "Missing logic"
+
+ /* Enable clocking */
+#warning "Missing logic"
+
+ /* Configure and enable LCD */
+ #warning "Missing logic"
+
+ return &g_lcddev.dev;
+}
diff --git a/nuttx/drivers/lcd/ssd1289.c b/nuttx/drivers/lcd/ssd1289.c
new file mode 100644
index 000000000..58c606968
--- /dev/null
+++ b/nuttx/drivers/lcd/ssd1289.c
@@ -0,0 +1,1279 @@
+/**************************************************************************************
+ * drivers/lcd/ssd1289.c
+ *
+ * Generic LCD driver for LCDs based on the Solomon Systech SSD1289 LCD controller.
+ * Think of this as a template for an LCD driver that you will proably ahve to
+ * customize for any particular LCD hardware.
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Authors: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: SSD1289, Rev 1.3, Apr 2007, Solomon Systech Limited
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Included Files
+ **************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1289.h>
+
+#include "ssd1289.h"
+
+#ifdef CONFIG_LCD_SSD1289
+
+/**************************************************************************************
+ * Pre-processor Definitions
+ **************************************************************************************/
+/* Configuration **********************************************************************/
+
+/* Check contrast selection */
+
+#if !defined(CONFIG_LCD_MAXCONTRAST)
+# define CONFIG_LCD_MAXCONTRAST 1
+#endif
+
+/* Check power setting */
+
+#if !defined(CONFIG_LCD_MAXPOWER) || CONFIG_LCD_MAXPOWER < 1
+# define CONFIG_LCD_MAXPOWER 1
+#endif
+
+#if CONFIG_LCD_MAXPOWER > 255
+# error "CONFIG_LCD_MAXPOWER must be less than 256 to fit in uint8_t"
+#endif
+
+/* Check orientation */
+
+#if defined(CONFIG_LCD_PORTRAIT)
+# if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE) || defined(CONFIG_LCD_RPORTRAIT)
+# error "Cannot define both portrait and any other orientations"
+# endif
+#elif defined(CONFIG_LCD_RPORTRAIT)
+# if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
+# error "Cannot define both rportrait and any other orientations"
+# endif
+#elif defined(CONFIG_LCD_LANDSCAPE)
+# ifdef CONFIG_LCD_RLANDSCAPE
+# error "Cannot define both landscape and any other orientations"
+# endif
+#elif !defined(CONFIG_LCD_RLANDSCAPE)
+# define CONFIG_LCD_LANDSCAPE 1
+#endif
+
+/* Define CONFIG_DEBUG_LCD to enable detailed LCD debug output. Verbose debug must
+ * also be enabled.
+ */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_DEBUG_GRAPHICS
+# undef CONFIG_DEBUG_LCD
+#endif
+
+#ifndef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_DEBUG_LCD
+#endif
+
+/* Display/Color Properties ***********************************************************/
+/* Display Resolution */
+
+#if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
+# define SSD1289_XRES 320
+# define SSD1289_YRES 240
+#else
+# define SSD1289_XRES 240
+# define SSD1289_YRES 320
+#endif
+
+/* Color depth and format */
+
+#define SSD1289_BPP 16
+#define SSD1289_COLORFMT FB_FMT_RGB16_565
+
+/* LCD Profiles ***********************************************************************/
+/* Many details of the controller initialization must, unfortunately, vary from LCD to
+ * LCD. I have looked at the spec and at three different drivers for LCDs that have
+ * SSD1289 controllers. I have tried to summarize these differences as "LCD profiles"
+ *
+ * Most of the differences between LCDs are nothing more than a few minor bit
+ * settings. The most significant difference betwen LCD drivers in is the
+ * manner in which the LCD is powered up and in how the power controls are set.
+ * My suggestion is that if you have working LCD initialization code, you should
+ * simply replace the code in ssd1289_hwinitialize with your working code.
+ */
+
+#if defined (CONFIG_SSD1289_PROFILE2)
+# undef SSD1289_USE_SIMPLE_INIT
+
+ /* PWRCTRL1: AP=smalll-to-medium, DC=Flinex24, BT=+5/-4, DCT=Flinex24 */
+
+# define PWRCTRL1_SETTING \
+ (SSD1289_PWRCTRL1_AP_SMMED | SSD1289_PWRCTRL1_DC_FLINEx24 | \
+ SSD1289_PWRCTRL1_BT_p5m4 | SSD1289_PWRCTRL1_DCT_FLINEx24)
+
+ /* PWRCTRL2: 5.1v */
+
+# define PWRCTRL2_SETTING SSD1289_PWRCTRL2_VRC_5p1V
+
+ /* PWRCTRL3: x 2.165
+ * NOTE: Many drivers have bit 8 set which is not defined in the SSD1289 spec.
+ */
+
+# define PWRCTRL3_SETTING SSD1289_PWRCTRL3_VRH_x2p165
+
+ /* PWRCTRL4: VDV=9 + VCOMG */
+
+# define PWRCTRL4_SETTING (SSD1289_PWRCTRL4_VDV(9) | SSD1289_PWRCTRL4_VCOMG)
+
+ /* PWRCTRL5: VCM=56 + NOTP */
+
+# define PWRCTRL5_SETTING (SSD1289_PWRCTRL5_VCM(56) | SSD1289_PWRCTRL5_NOTP)
+
+#elif defined (CONFIG_SSD1289_PROFILE3)
+# undef SSD1289_USE_SIMPLE_INIT
+
+ /* PWRCTRL1: AP=smalll-to-medium, DC=Flinex24, BT=+5/-4, DCT=Flinex24 */
+
+# define PWRCTRL1_SETTING \
+ (SSD1289_PWRCTRL1_AP_SMMED | SSD1289_PWRCTRL1_DC_FLINEx24 | \
+ SSD1289_PWRCTRL1_BT_p5m4 | SSD1289_PWRCTRL1_DCT_FLINEx24)
+
+ /* PWRCTRL2: 5.1v */
+
+# define PWRCTRL2_SETTING SSD1289_PWRCTRL2_VRC_5p1V
+
+ /* PWRCTRL3: x 2.165
+ * NOTE: Many drivers have bit 8 set which is not defined in the SSD1289 spec.
+ */
+
+# define PWRCTRL3_SETTING SSD1289_PWRCTRL3_VRH_x2p165
+
+ /* PWRCTRL4: VDV=9 + VCOMG */
+
+# define PWRCTRL4_SETTING (SSD1289_PWRCTRL4_VDV(9) | SSD1289_PWRCTRL4_VCOMG)
+
+ /* PWRCTRL5: VCM=56 + NOTP */
+
+# define PWRCTRL5_SETTING (SSD1289_PWRCTRL5_VCM(56) | SSD1289_PWRCTRL5_NOTP)
+
+#else /* if defined (CONFIG_SSD1289_PROFILE1) */
+# undef SSD1289_USE_SIMPLE_INIT
+# define SSD1289_USE_SIMPLE_INIT 1
+
+ /* PWRCTRL1: AP=medium-to-large, DC=Fosc/4, BT=+5/-4, DCT=Fosc/4 */
+
+# define PWRCTRL1_SETTING \
+ (SSD1289_PWRCTRL1_AP_MEDLG | SSD1289_PWRCTRL1_DC_FOSd4 | \
+ SSD1289_PWRCTRL1_BT_p5m4 | SSD1289_PWRCTRL1_DCT_FOSd4)
+
+ /* PWRCTRL2: 5.3v */
+
+# define PWRCTRL2_SETTING SSD1289_PWRCTRL2_VRC_5p3V
+
+ /* PWRCTRL3: x 2.570
+ * NOTE: Many drivers have bit 8 set which is not defined in the SSD1289 spec.
+ */
+
+# define PWRCTRL3_SETTING SSD1289_PWRCTRL3_VRH_x2p570
+
+ /* PWRCTRL4: VDV=12 + VCOMG */
+
+# define PWRCTRL4_SETTING (SSD1289_PWRCTRL4_VDV(12) | SSD1289_PWRCTRL4_VCOMG)
+
+ /* PWRCTRL5: VCM=60 + NOTP */
+
+# define PWRCTRL5_SETTING (SSD1289_PWRCTRL5_VCM(60) | SSD1289_PWRCTRL5_NOTP)
+
+#endif
+
+/* Debug ******************************************************************************/
+
+#ifdef CONFIG_DEBUG_LCD
+# define lcddbg dbg
+# define lcdvdbg vdbg
+#else
+# define lcddbg(x...)
+# define lcdvdbg(x...)
+#endif
+
+/**************************************************************************************
+ * Private Type Definition
+ **************************************************************************************/
+
+/* This structure describes the state of this driver */
+
+struct ssd1289_dev_s
+{
+ /* Publically visible device structure */
+
+ struct lcd_dev_s dev;
+
+ /* Private LCD-specific information follows */
+
+ FAR struct ssd1289_lcd_s *lcd; /* The contained platform-specific, LCD interface */
+ uint8_t power; /* Current power setting */
+
+ /* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane. This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8). Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently. However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+ uint16_t runbuffer[SSD1289_XRES];
+};
+
+/**************************************************************************************
+ * Private Function Protototypes
+ **************************************************************************************/
+/* Low Level LCD access */
+
+static void ssd1289_putreg(FAR struct ssd1289_lcd_s *lcd, uint8_t regaddr,
+ uint16_t regval);
+#ifndef CONFIG_LCD_NOGETRUN
+static uint16_t ssd1289_readreg(FAR struct ssd1289_lcd_s *lcd, uint8_t regaddr);
+#endif
+static inline void ssd1289_gramwrite(FAR struct ssd1289_lcd_s *lcd, uint16_t rgbcolor);
+#ifndef CONFIG_LCD_NOGETRUN
+static inline void ssd1289_readsetup(FAR struct ssd1289_lcd_s *lcd, FAR uint16_t *accum);
+static inline uint16_t ssd1289_gramread(FAR struct ssd1289_lcd_s *lcd, FAR uint16_t *accum);
+#endif
+static void ssd1289_setcursor(FAR struct ssd1289_lcd_s *lcd, uint16_t column,
+ uint16_t row);
+
+/* LCD Data Transfer Methods */
+
+static int ssd1289_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels);
+static int ssd1289_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1289_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo);
+static int ssd1289_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+# error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+# error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1289_getpower(FAR struct lcd_dev_s *dev);
+static int ssd1289_setpower(FAR struct lcd_dev_s *dev, int power);
+static int ssd1289_getcontrast(FAR struct lcd_dev_s *dev);
+static int ssd1289_setcontrast(FAR struct lcd_dev_s *dev, unsigned int contrast);
+
+/* Initialization */
+
+static inline int ssd1289_hwinitialize(FAR struct ssd1289_dev_s *priv);
+
+/**************************************************************************************
+ * Private Data
+ **************************************************************************************/
+
+/* This driver can support only a signal SSD1289 device. This is due to an
+ * unfortunate decision made whent he getrun and putrun methods were designed. The
+ * following is the single SSD1289 driver state instance:
+ */
+
+static struct ssd1289_dev_s g_lcddev;
+
+/**************************************************************************************
+ * Private Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: ssd1289_putreg(lcd,
+ *
+ * Description:
+ * Write to an LCD register
+ *
+ **************************************************************************************/
+
+static void ssd1289_putreg(FAR struct ssd1289_lcd_s *lcd, uint8_t regaddr, uint16_t regval)
+{
+ /* Set the index register to the register address and write the register contents */
+
+ lcd->index(lcd, regaddr);
+ lcd->write(lcd, regval);
+}
+
+/**************************************************************************************
+ * Name: ssd1289_readreg
+ *
+ * Description:
+ * Read from an LCD register
+ *
+ **************************************************************************************/
+
+#ifndef CONFIG_LCD_NOGETRUN
+static uint16_t ssd1289_readreg(FAR struct ssd1289_lcd_s *lcd, uint8_t regaddr)
+{
+ /* Set the index register to the register address and read the register contents */
+
+ lcd->index(lcd, regaddr);
+ return lcd->read(lcd);
+}
+#endif
+
+/**************************************************************************************
+ * Name: ssd1289_gramselect
+ *
+ * Description:
+ * Setup to read or write multiple pixels to the GRAM memory
+ *
+ **************************************************************************************/
+
+static inline void ssd1289_gramselect(FAR struct ssd1289_lcd_s *lcd)
+{
+ lcd->index(lcd, SSD1289_DATA);
+}
+
+/**************************************************************************************
+ * Name: ssd1289_gramwrite
+ *
+ * Description:
+ * Setup to read or write multiple pixels to the GRAM memory
+ *
+ **************************************************************************************/
+
+static inline void ssd1289_gramwrite(FAR struct ssd1289_lcd_s *lcd, uint16_t data)
+{
+ lcd->write(lcd, data);
+}
+
+/**************************************************************************************
+ * Name: ssd1289_readsetup
+ *
+ * Description:
+ * Prime the operation by reading one pixel from the GRAM memory if necessary for
+ * this LCD type. When reading 16-bit gram data, there may be some shifts in the
+ * returned data:
+ *
+ * - ILI932x: Discard first dummy read; no shift in the return data
+ *
+ **************************************************************************************/
+
+#ifndef CONFIG_LCD_NOGETRUN
+static inline void ssd1289_readsetup(FAR struct ssd1289_lcd_s *lcd, FAR uint16_t *accum)
+{
+ /* Read-ahead one pixel */
+
+ *accum = lcd->read(lcd);
+}
+#endif
+
+/**************************************************************************************
+ * Name: ssd1289_gramread
+ *
+ * Description:
+ * Read one correctly aligned pixel from the GRAM memory. Possibly shifting the
+ * data and possibly swapping red and green components.
+ *
+ * - ILI932x: Unknown -- assuming colors are in the color order
+ *
+ **************************************************************************************/
+
+#ifndef CONFIG_LCD_NOGETRUN
+static inline uint16_t ssd1289_gramread(FAR struct ssd1289_lcd_s *lcd, FAR uint16_t *accum)
+{
+ /* Read the value (GRAM register already selected) */
+
+ return lcd->read(lcd);
+}
+#endif
+
+/**************************************************************************************
+ * Name: ssd1289_setcursor
+ *
+ * Description:
+ * Set the cursor position. In landscape mode, the "column" is actually the physical
+ * Y position and the "row" is the physical X position.
+ *
+ **************************************************************************************/
+
+static void ssd1289_setcursor(FAR struct ssd1289_lcd_s *lcd, uint16_t column, uint16_t row)
+{
+#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
+ ssd1289_putreg(lcd, SSD1289_XADDR, column); /* 0-239 */
+ ssd1289_putreg(lcd, SSD1289_YADDR, row); /* 0-319 */
+#elif defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
+ ssd1289_putreg(lcd, SSD1289_XADDR, row); /* 0-239 */
+ ssd1289_putreg(lcd, SSD1289_YADDR, column); /* 0-319 */
+#endif
+}
+
+/**************************************************************************************
+ * Name: ssd1289_dumprun
+ *
+ * Description:
+ * Dump the contexts of the run buffer:
+ *
+ * run - The buffer in containing the run read to be dumped
+ * npixels - The number of pixels to dump
+ *
+ **************************************************************************************/
+
+#if 0 /* Sometimes useful */
+static void ssd1289_dumprun(FAR const char *msg, FAR uint16_t *run, size_t npixels)
+{
+ int i, j;
+
+ lib_rawprintf("\n%s:\n", msg);
+ for (i = 0; i < npixels; i += 16)
+ {
+ up_putc(' ');
+ lib_rawprintf(" ");
+ for (j = 0; j < 16; j++)
+ {
+ lib_rawprintf(" %04x", *run++);
+ }
+ up_putc('\n');
+ }
+}
+#endif
+
+/**************************************************************************************
+ * Name: ssd1289_putrun
+ *
+ * Description:
+ * This method can be used to write a partial raster line to the LCD:
+ *
+ * row - Starting row to write to (range: 0 <= row < yres)
+ * col - Starting column to write to (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer containing the run to be written to the LCD
+ * npixels - The number of pixels to write to the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+static int ssd1289_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels)
+{
+ FAR struct ssd1289_dev_s *priv = &g_lcddev;
+ FAR struct ssd1289_lcd_s *lcd = priv->lcd;
+ FAR const uint16_t *src = (FAR const uint16_t*)buffer;
+ int i;
+
+ /* Buffer must be provided and aligned to a 16-bit address boundary */
+
+ lcdvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);
+
+ /* Select the LCD */
+
+ lcd->select(lcd);
+
+ /* Write the run to GRAM. */
+
+#ifdef CONFIG_LCD_LANDSCAPE
+ /* Convert coordinates -- Here the edge away from the row of buttons on
+ * the STM3240G-EVAL is used as the top.
+ */
+
+ /* Write the GRAM data, manually incrementing X */
+
+ for (i = 0; i < npixels; i++)
+ {
+ /* Write the next pixel to this position */
+
+ ssd1289_setcursor(lcd, col, row);
+ ssd1289_gramselect(lcd);
+ ssd1289_gramwrite(lcd, *src);
+
+ /* Increment to next column */
+
+ src++;
+ col++;
+ }
+#elif defined(CONFIG_LCD_RLANDSCAPE)
+ /* Convert coordinates -- Here the edge next to the row of buttons on
+ * the STM3240G-EVAL is used as the top.
+ */
+
+ col = (SSD1289_XRES-1) - col;
+ row = (SSD1289_YRES-1) - row;
+
+ /* Set the cursor position */
+
+ ssd1289_setcursor(lcd, col, row);
+
+ /* Then write the GRAM data, auto-decrementing X */
+
+ ssd1289_gramselect(lcd);
+ for (i = 0; i < npixels; i++)
+ {
+ /* Write the next pixel to this position (auto-decrements to the next column) */
+
+ ssd1289_gramwrite(lcd, *src);
+ src++;
+ }
+#elif defined(CONFIG_LCD_PORTRAIT)
+ /* Convert coordinates. In this configuration, the top of the display is to the left
+ * of the buttons (if the board is held so that the buttons are at the botton of the
+ * board).
+ */
+
+ col = (SSD1289_XRES-1) - col;
+
+ /* Then write the GRAM data, manually incrementing Y (which is col) */
+
+ for (i = 0; i < npixels; i++)
+ {
+ /* Write the next pixel to this position */
+
+ ssd1289_setcursor(lcd, row, col);
+ ssd1289_gramselect(lcd);
+ ssd1289_gramwrite(lcd, *src);
+
+ /* Increment to next column */
+
+ src++;
+ col--;
+ }
+#else /* CONFIG_LCD_RPORTRAIT */
+ /* Convert coordinates. In this configuration, the top of the display is to the right
+ * of the buttons (if the board is held so that the buttons are at the botton of the
+ * board).
+ */
+
+ row = (SSD1289_YRES-1) - row;
+
+ /* Then write the GRAM data, manually incrementing Y (which is col) */
+
+ for (i = 0; i < npixels; i++)
+ {
+ /* Write the next pixel to this position */
+
+ ssd1289_setcursor(lcd, row, col);
+ ssd1289_gramselect(lcd);
+ ssd1289_gramwrite(lcd, *src);
+
+ /* Decrement to next column */
+
+ src++;
+ col++;
+ }
+#endif
+
+ /* De-select the LCD */
+
+ lcd->deselect(lcd);
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: ssd1289_getrun
+ *
+ * Description:
+ * This method can be used to read a partial raster line from the LCD:
+ *
+ * row - Starting row to read from (range: 0 <= row < yres)
+ * col - Starting column to read read (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer in which to return the run read from the LCD
+ * npixels - The number of pixels to read from the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+static int ssd1289_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels)
+{
+#ifndef CONFIG_LCD_NOGETRUN
+ FAR struct ssd1289_dev_s *priv = &g_lcddev;
+ FAR struct ssd1289_lcd_s *lcd = priv->lcd;
+ FAR uint16_t *dest = (FAR uint16_t*)buffer;
+ uint16_t accum;
+ int i;
+
+ /* Buffer must be provided and aligned to a 16-bit address boundary */
+
+ lcdvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);
+
+ /* Select the LCD */
+
+ lcd->select(lcd);
+
+ /* Read the run from GRAM. */
+
+#ifdef CONFIG_LCD_LANDSCAPE
+ /* Convert coordinates -- Here the edge away from the row of buttons on
+ * the STM3240G-EVAL is used as the top.
+ */
+
+ for (i = 0; i < npixels; i++)
+ {
+ /* Read the next pixel from this position */
+
+ ssd1289_setcursor(lcd, row, col);
+ ssd1289_gramselect(lcd);
+ ssd1289_readsetup(lcd, &accum);
+ *dest++ = ssd1289_gramread(lcd, &accum);
+
+ /* Increment to next column */
+
+ col++;
+ }
+#elif defined(CONFIG_LCD_RLANDSCAPE)
+ /* Convert coordinates -- Here the edge next to the row of buttons on
+ * the STM3240G-EVAL is used as the top.
+ */
+
+ col = (SSD1289_XRES-1) - col;
+ row = (SSD1289_YRES-1) - row;
+
+ /* Set the cursor position */
+
+ ssd1289_setcursor(lcd, col, row);
+
+ /* Then read the GRAM data, auto-decrementing Y */
+
+ ssd1289_gramselect(lcd);
+
+ /* Prime the pump for unaligned read data */
+
+ ssd1289_readsetup(lcd, &accum);
+
+ for (i = 0; i < npixels; i++)
+ {
+ /* Read the next pixel from this position (autoincrements to the next row) */
+
+ *dest++ = ssd1289_gramread(lcd, &accum);
+ }
+#elif defined(CONFIG_LCD_PORTRAIT)
+ /* Convert coordinates. In this configuration, the top of the display is to the left
+ * of the buttons (if the board is held so that the buttons are at the botton of the
+ * board).
+ */
+
+ col = (SSD1289_XRES-1) - col;
+
+ /* Then read the GRAM data, manually incrementing Y (which is col) */
+
+ for (i = 0; i < npixels; i++)
+ {
+ /* Read the next pixel from this position */
+
+ ssd1289_setcursor(lcd, row, col);
+ ssd1289_gramselect(lcd);
+ ssd1289_readsetup(lcd, &accum);
+ *dest++ = ssd1289_gramread(lcd, &accum);
+
+ /* Increment to next column */
+
+ col--;
+ }
+#else /* CONFIG_LCD_RPORTRAIT */
+ /* Convert coordinates. In this configuration, the top of the display is to the right
+ * of the buttons (if the board is held so that the buttons are at the botton of the
+ * board).
+ */
+
+ row = (SSD1289_YRES-1) - row;
+
+ /* Then write the GRAM data, manually incrementing Y (which is col) */
+
+ for (i = 0; i < npixels; i++)
+ {
+ /* Write the next pixel to this position */
+
+ ssd1289_setcursor(lcd, row, col);
+ ssd1289_gramselect(lcd);
+ ssd1289_readsetup(lcd, &accum);
+ *dest++ = ssd1289_gramread(lcd, &accum);
+
+ /* Decrement to next column */
+
+ col++;
+ }
+#endif
+
+ /* De-select the LCD */
+
+ lcd->deselect(lcd);
+ return OK;
+#else
+ return -ENOSYS;
+#endif
+}
+
+/**************************************************************************************
+ * Name: ssd1289_getvideoinfo
+ *
+ * Description:
+ * Get information about the LCD video controller configuration.
+ *
+ **************************************************************************************/
+
+static int ssd1289_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo)
+{
+ DEBUGASSERT(dev && vinfo);
+ lcdvdbg("fmt: %d xres: %d yres: %d nplanes: 1\n",
+ SSD1289_COLORFMT, SSD1289_XRES, SSD1289_XRES);
+
+ vinfo->fmt = SSD1289_COLORFMT; /* Color format: RGB16-565: RRRR RGGG GGGB BBBB */
+ vinfo->xres = SSD1289_XRES; /* Horizontal resolution in pixel columns */
+ vinfo->yres = SSD1289_YRES; /* Vertical resolution in pixel rows */
+ vinfo->nplanes = 1; /* Number of color planes supported */
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: ssd1289_getplaneinfo
+ *
+ * Description:
+ * Get information about the configuration of each LCD color plane.
+ *
+ **************************************************************************************/
+
+static int ssd1289_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo)
+{
+ FAR struct ssd1289_dev_s *priv = (FAR struct ssd1289_dev_s *)dev;
+
+ DEBUGASSERT(dev && pinfo && planeno == 0);
+ lcdvdbg("planeno: %d bpp: %d\n", planeno, SSD1289_BPP);
+
+ pinfo->putrun = ssd1289_putrun; /* Put a run into LCD memory */
+ pinfo->getrun = ssd1289_getrun; /* Get a run from LCD memory */
+ pinfo->buffer = (uint8_t*)priv->runbuffer; /* Run scratch buffer */
+ pinfo->bpp = SSD1289_BPP; /* Bits-per-pixel */
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: ssd1289_getpower
+ *
+ * Description:
+ * Get the LCD panel power status (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int ssd1289_getpower(FAR struct lcd_dev_s *dev)
+{
+ lcdvdbg("power: %d\n", 0);
+ return g_lcddev.power;
+}
+
+/**************************************************************************************
+ * Name: ssd1289_poweroff
+ *
+ * Description:
+ * Enable/disable LCD panel power (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int ssd1289_poweroff(FAR struct ssd1289_lcd_s *lcd)
+{
+ /* Set the backlight off */
+
+ lcd->backlight(lcd, 0);
+
+ /* Turn the display off */
+
+ ssd1289_putreg(lcd, SSD1289_DSPCTRL, 0);
+
+ /* Remember the power off state */
+
+ g_lcddev.power = 0;
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: ssd1289_setpower
+ *
+ * Description:
+ * Enable/disable LCD panel power (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int ssd1289_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+ FAR struct ssd1289_dev_s *priv = (FAR struct ssd1289_dev_s *)dev;
+ FAR struct ssd1289_lcd_s *lcd = priv->lcd;
+
+ lcdvdbg("power: %d\n", power);
+ DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER);
+
+ /* Set new power level */
+
+ if (power > 0)
+ {
+ /* Set the backlight level */
+
+ lcd->backlight(lcd, power);
+
+ /* Then turn the display on:
+ * D=ON(3) CM=0 DTE=1 GON=1 SPT=0 VLE=0 PT=0
+ */
+
+ ssd1289_putreg(lcd, SSD1289_DSPCTRL,
+ (SSD1289_DSPCTRL_ON | SSD1289_DSPCTRL_GON |
+ SSD1289_DSPCTRL_DTE | SSD1289_DSPCTRL_VLE(0)));
+
+ g_lcddev.power = power;
+ }
+ else
+ {
+ /* Turn the display off */
+
+ ssd1289_poweroff(lcd);
+ }
+
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: ssd1289_getcontrast
+ *
+ * Description:
+ * Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int ssd1289_getcontrast(FAR struct lcd_dev_s *dev)
+{
+ lcdvdbg("Not implemented\n");
+ return -ENOSYS;
+}
+
+/**************************************************************************************
+ * Name: ssd1289_setcontrast
+ *
+ * Description:
+ * Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int ssd1289_setcontrast(FAR struct lcd_dev_s *dev, unsigned int contrast)
+{
+ lcdvdbg("contrast: %d\n", contrast);
+ return -ENOSYS;
+}
+
+/**************************************************************************************
+ * Name: ssd1289_hwinitialize
+ *
+ * Description:
+ * Initialize the LCD hardware.
+ *
+ **************************************************************************************/
+
+static inline int ssd1289_hwinitialize(FAR struct ssd1289_dev_s *priv)
+{
+ FAR struct ssd1289_lcd_s *lcd = priv->lcd;
+#ifndef CONFIG_LCD_NOGETRUN
+ uint16_t id;
+#endif
+
+ /* Select the LCD */
+
+ lcd->select(lcd);
+
+#ifndef CONFIG_LCD_NOGETRUN
+ id = ssd1289_readreg(lcd, SSD1289_DEVCODE);
+ lcddbg("LCD ID: %04x\n", id);
+
+ /* Check if the ID is for the SSD1289 */
+
+ if (id == SSD1289_DEVCODE_VALUE)
+#endif
+ {
+ /* LCD controller configuration. Many details of the controller initialization
+ * must, unfortunately, vary from LCD to LCD. I have looked at the spec and at
+ * three different drivers for LCDs that have SSD1289 controllers. I have tried
+ * to summarize these differences as profiles (defined above). Some other
+ * alternatives are noted below.
+ *
+ * Most of the differences between LCDs are nothing more than a few minor bit
+ * settings. The most significant difference betwen LCD drivers in is the
+ * manner in which the LCD is powered up and in how the power controls are set.
+ * My suggestion is that if you have working LCD initialization code, you should
+ * simply replace the following guesses with your working code.
+ */
+
+ /* Most drivers just enable the oscillator */
+
+#ifdef SSD1289_USE_SIMPLE_INIT
+ ssd1289_putreg(lcd, SSD1289_OSCSTART, SSD1289_OSCSTART_OSCEN);
+#else
+ /* But one goes through a more complex start-up sequence. Something like the
+ * following:
+ *
+ * First, put the display in INTERNAL operation:
+ * D=INTERNAL(1) CM=0 DTE=0 GON=1 SPT=0 VLE=0 PT=0
+ */
+
+ ssd1289_putreg(lcd, SSD1289_DSPCTRL,
+ (SSD1289_DSPCTRL_INTERNAL | SSD1289_DSPCTRL_GON |
+ SSD1289_DSPCTRL_VLE(0)));
+
+ /* Then enable the oscillator */
+
+ ssd1289_putreg(lcd, SSD1289_OSCSTART, SSD1289_OSCSTART_OSCEN);
+
+ /* Turn the display on:
+ * D=ON(3) CM=0 DTE=0 GON=1 SPT=0 VLE=0 PT=0
+ */
+
+ ssd1289_putreg(lcd, SSD1289_DSPCTRL,
+ (SSD1289_DSPCTRL_ON | SSD1289_DSPCTRL_GON |
+ SSD1289_DSPCTRL_VLE(0)));
+
+ /* Take the LCD out of sleep mode */
+
+ ssd1289_putreg(lcd, SSD1289_SLEEP, 0);
+ up_mdelay(30);
+
+ /* Turn the display on:
+ * D=INTERNAL(1) CM=0 DTE=1 GON=1 SPT=0 VLE=0 PT=0
+ */
+
+ ssd1289_putreg(lcd, SSD1289_DSPCTRL,
+ (SSD1289_DSPCTRL_ON | SSD1289_DSPCTRL_DTE |
+ SSD1289_DSPCTRL_GON | SSD1289_DSPCTRL_VLE(0)));
+#endif
+
+ /* Set up power control registers. There is a lot of variability
+ * from LCD-to-LCD in how the power registers are configured.
+ */
+
+ ssd1289_putreg(lcd, SSD1289_PWRCTRL1, PWRCTRL1_SETTING);
+ ssd1289_putreg(lcd, SSD1289_PWRCTRL2, PWRCTRL2_SETTING);
+
+ /* One driver adds a delay here.. I doubt that this is really necessary. */
+ /* up_mdelay(15); */
+
+ ssd1289_putreg(lcd, SSD1289_PWRCTRL3, PWRCTRL3_SETTING);
+ ssd1289_putreg(lcd, SSD1289_PWRCTRL4, PWRCTRL4_SETTING);
+ ssd1289_putreg(lcd, SSD1289_PWRCTRL5, PWRCTRL5_SETTING);
+
+ /* One driver does an odd setting of the the driver output control.
+ * No idea why.
+ */
+#if 0
+ ssd1289_putreg(lcd, SSD1289_OUTCTRL,
+ (SSD1289_OUTCTRL_MUX(12) | SSD1289_OUTCTRL_TB |
+ SSD1289_OUTCTRL_BGR | SSD1289_OUTCTRL_CAD));
+
+ /* The same driver does another small delay here */
+
+ up_mdelay(15);
+#endif
+
+ /* After this point, the drivers differ only in some varying register
+ * bit settings.
+ */
+
+ /* Set the driver output control.
+ * PORTRAIT MODES:
+ * MUX=319, TB=1, SM=0, BGR=1, CAD=0, REV=1, RL=0
+ * LANDSCAPE MODES:
+ * MUX=319, TB=0, SM=0, BGR=1, CAD=0, REV=1, RL=0
+ */
+
+#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
+ ssd1289_putreg(lcd, SSD1289_OUTCTRL,
+ (SSD1289_OUTCTRL_MUX(319) | SSD1289_OUTCTRL_TB |
+ SSD1289_OUTCTRL_BGR | SSD1289_OUTCTRL_REV);
+#else
+ ssd1289_putreg(lcd, SSD1289_OUTCTRL,
+ (SSD1289_OUTCTRL_MUX(319) | SSD1289_OUTCTRL_BGR |
+ SSD1289_OUTCTRL_REV));
+#endif
+
+ /* Set the LCD driving AC waveform
+ * NW=0, WSMD=0, EOR=1, BC=1, ENWD=0, FLD=0
+ */
+
+ ssd1289_putreg(lcd, SSD1289_ACCTRL,
+ (SSD1289_ACCTRL_EOR | SSD1289_ACCTRL_BC));
+
+ /* Take the LCD out of sleep mode (isn't this redundant in the non-
+ * simple case?)
+ */
+
+ ssd1289_putreg(lcd, SSD1289_SLEEP, 0);
+
+ /* Set entry mode */
+
+#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
+ /* LG=0, AM=0, ID=3, TY=2, DMODE=0, WMODE=0, OEDEF=0, TRANS=0, DRM=3
+ * Alternative TY=2 (But TY only applies in 262K color mode anyway)
+ */
+
+ ssd1289_putreg(lcd, SSD1289_ENTRY,
+ (SSD1289_ENTRY_ID_HINCVINC | SSD1289_ENTRY_TY_C |
+ SSD1289_ENTRY_DMODE_RAM | SSD1289_ENTRY_DFM_65K));
+#else
+ /* LG=0, AM=1, ID=3, TY=2, DMODE=0, WMODE=0, OEDEF=0, TRANS=0, DRM=3 */
+ /* Alternative TY=2 (But TY only applies in 262K color mode anyway) */
+
+ ssd1289_putreg(lcd, SSD1289_ENTRY,
+ (SSD1289_ENTRY_AM | SSD1289_ENTRY_ID_HINCVINC |
+ SSD1289_ENTRY_TY_C | SSD1289_ENTRY_DMODE_RAM |
+ SSD1289_ENTRY_DFM_65K));
+#endif
+
+ /* Clear compare registers */
+
+ ssd1289_putreg(lcd, SSD1289_CMP1, 0);
+ ssd1289_putreg(lcd, SSD1289_CMP2, 0);
+
+ /* One driver puts a huge, 100 millisecond delay here */
+ /* up_mdelay(100); */
+
+ /* Set Horizontal and vertical porch.
+ * Horizontal porch: 239 pixels per line, delay=28
+ * Vertical porch: VBP=3, XFP=0
+ */
+
+ ssd1289_putreg(lcd, SSD1289_HPORCH,
+ (28 << SSD1289_HPORCH_HBP_SHIFT) | (239 << SSD1289_HPORCH_XL_SHIFT));
+ ssd1289_putreg(lcd, SSD1289_VPORCH,
+ (3 << SSD1289_VPORCH_VBP_SHIFT) | (0 << SSD1289_VPORCH_XFP_SHIFT));
+
+ /* Set display control.
+ * D=ON(3), CM=0 (not 8-color), DTE=1, GON=1, SPT=0, VLE=1 PT=0
+ */
+
+ ssd1289_putreg(lcd, SSD1289_DSPCTRL,
+ (SSD1289_DSPCTRL_ON | SSD1289_DSPCTRL_DTE |
+ SSD1289_DSPCTRL_GON | SSD1289_DSPCTRL_VLE(1)));
+
+ /* Frame cycle control. Alternative: SSD1289_FCYCCTRL_DIV8 */
+
+ ssd1289_putreg(lcd, SSD1289_FCYCCTRL, 0);
+
+ /* Gate scan start position = 0 */
+
+ ssd1289_putreg(lcd, SSD1289_GSTART, 0);
+
+ /* Clear vertical scrolling */
+
+ ssd1289_putreg(lcd, SSD1289_VSCROLL1, 0);
+ ssd1289_putreg(lcd, SSD1289_VSCROLL2, 0);
+
+ /* Setup window 1 (0-319) */
+
+ ssd1289_putreg(lcd, SSD1289_W1START, 0);
+ ssd1289_putreg(lcd, SSD1289_W1END, 319);
+
+ /* Disable window 2 (0-0) */
+
+ ssd1289_putreg(lcd, SSD1289_W2START, 0);
+ ssd1289_putreg(lcd, SSD1289_W2END, 0);
+
+ /* Horizontal start and end (0-239) */
+
+ ssd1289_putreg(lcd, SSD1289_HADDR,
+ (0 << SSD1289_HADDR_HSA_SHIFT) | (239 << SSD1289_HADDR_HEA_SHIFT));
+
+ /* Vertical start and end (0-319) */
+
+ ssd1289_putreg(lcd, SSD1289_VSTART, 0);
+ ssd1289_putreg(lcd, SSD1289_VEND, 319);
+
+ /* Gamma controls */
+
+ ssd1289_putreg(lcd, SSD1289_GAMMA1, 0x0707);
+ ssd1289_putreg(lcd, SSD1289_GAMMA2, 0x0204); /* Alternative: 0x0704 */
+ ssd1289_putreg(lcd, SSD1289_GAMMA3, 0x0204);
+ ssd1289_putreg(lcd, SSD1289_GAMMA4, 0x0502);
+ ssd1289_putreg(lcd, SSD1289_GAMMA5, 0x0507);
+ ssd1289_putreg(lcd, SSD1289_GAMMA6, 0x0204);
+ ssd1289_putreg(lcd, SSD1289_GAMMA7, 0x0204);
+ ssd1289_putreg(lcd, SSD1289_GAMMA8, 0x0502);
+ ssd1289_putreg(lcd, SSD1289_GAMMA9, 0x0302);
+ ssd1289_putreg(lcd, SSD1289_GAMMA10, 0x0302); /* Alternative: 0x1f00 */
+
+ /* Clear write mask */
+
+ ssd1289_putreg(lcd, SSD1289_WRMASK1, 0);
+ ssd1289_putreg(lcd, SSD1289_WRMASK2, 0);
+
+ /* Set frame frequency = 65Hz (This should not be necessary since this
+ * is the default POR value)
+ */
+
+ ssd1289_putreg(lcd, SSD1289_FFREQ, SSD1289_FFREQ_OSC_FF65);
+
+ /* Set the cursor at the home position and set the index register to
+ * the gram data register (I can't imagine these are necessary).
+ */
+
+ ssd1289_setcursor(lcd, 0, 0);
+ ssd1289_gramselect(lcd);
+
+ /* One driver has a 50 msec delay here */
+ /* up_mdelay(50); */
+
+ return OK;
+ }
+#ifndef CONFIG_LCD_NOGETRUN
+ else
+ {
+ lcddbg("Unsupported LCD type\n");
+ return -ENODEV;
+ }
+#endif
+
+ /* De-select the LCD */
+
+ lcd->deselect(lcd);
+}
+
+ /*************************************************************************************
+ * Public Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: ssd1289_lcdinitialize
+ *
+ * Description:
+ * Initialize the LCD video hardware. The initial state of the LCD is fully
+ * initialized, display memory cleared, and the LCD ready to use, but with the power
+ * setting at 0 (full off).
+ *
+ **************************************************************************************/
+
+FAR struct lcd_dev_s *ssd1289_lcdinitialize(FAR struct ssd1289_lcd_s *lcd)
+{
+ int ret;
+
+ lcdvdbg("Initializing\n");
+
+ /* If we ccould support multiple SSD1289 devices, this is where we would allocate
+ * a new driver data structure... but we can't. Why not? Because of a bad should
+ * the form of the getrun() and putrun methods.
+ */
+
+ FAR struct ssd1289_dev_s *priv = &g_lcddev;
+
+ /* Initialize the driver data structure */
+
+ priv->dev.getvideoinfo = ssd1289_getvideoinfo;
+ priv->dev.getplaneinfo = ssd1289_getplaneinfo;
+ priv->dev.getpower = ssd1289_getpower;
+ priv->dev.setpower = ssd1289_setpower;
+ priv->dev.getcontrast = ssd1289_getcontrast;
+ priv->dev.setcontrast = ssd1289_setcontrast;
+ priv->lcd = lcd;
+
+ /* Configure and enable LCD */
+
+ ret = ssd1289_hwinitialize(priv);
+ if (ret == OK)
+ {
+ /* Clear the display (setting it to the color 0=black) */
+
+ ssd1289_clear(&priv->dev, 0);
+
+ /* Turn the display off */
+
+ ssd1289_poweroff(lcd);
+ return &g_lcddev.dev;
+ }
+
+ return NULL;
+}
+
+/**************************************************************************************
+ * Name: ssd1289_clear
+ *
+ * Description:
+ * This is a non-standard LCD interface just for the stm3240g-EVAL board. Because
+ * of the various rotations, clearing the display in the normal way by writing a
+ * sequences of runs that covers the entire display can be very slow. Here the
+ * display is cleared by simply setting all GRAM memory to the specified color.
+ *
+ **************************************************************************************/
+
+void ssd1289_clear(FAR struct lcd_dev_s *dev, uint16_t color)
+{
+ FAR struct ssd1289_dev_s *priv = (FAR struct ssd1289_dev_s *)dev;
+ FAR struct ssd1289_lcd_s *lcd = priv->lcd;
+ uint32_t i;
+
+ /* Select the LCD and home the cursor position */
+
+ lcd->select(lcd);
+ ssd1289_setcursor(lcd, 0, 0);
+
+ /* Prepare to write GRAM data */
+
+ ssd1289_gramselect(lcd);
+
+ /* Copy color into all of GRAM. Orientation does not matter in this case. */
+
+ for (i = 0; i < SSD1289_XRES * SSD1289_YRES; i++)
+ {
+ ssd1289_gramwrite(lcd, color);
+ }
+
+ /* De-select the LCD */
+
+ lcd->deselect(lcd);
+}
+
+#endif /* CONFIG_LCD_SSD1289 */
diff --git a/nuttx/drivers/lcd/ssd1289.h b/nuttx/drivers/lcd/ssd1289.h
new file mode 100644
index 000000000..6d5d1c3cb
--- /dev/null
+++ b/nuttx/drivers/lcd/ssd1289.h
@@ -0,0 +1,425 @@
+/**************************************************************************************
+ * drivers/lcd/ssd1289.h
+ * Definitions for the Solomon Systech SSD1289 LCD controller
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: SSD1289, Rev 1.3, Apr 2007, Solomon Systech Limited
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ **************************************************************************************/
+
+#ifndef __DRIVERS_LCD_SSD1289_H
+#define __DRIVERS_LCD_SSD1289_H
+
+/**************************************************************************************
+ * Included Files
+ **************************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_LCD_SSD1289
+
+/**************************************************************************************
+ * Pre-processor Definitions
+ **************************************************************************************/
+
+/* SSD1289 Register Addresses (All with DC=1) */
+
+#define SSD1289_OSCSTART 0x00 /* Oscillation Start (write) */
+#define SSD1289_DEVCODE 0x00 /* Oscillation Start (read) */
+#define SSD1289_OUTCTRL 0x01 /* Driver output control */
+#define SSD1289_ACCTRL 0x02 /* LCD drive AC control */
+#define SSD1289_PWRCTRL1 0x03 /* Power control 1 */
+#define SSD1289_CMP1 0x05 /* Compare register 1 */
+#define SSD1289_CMP2 0x06 /* Compare register 2 */
+#define SSD1289_DSPCTRL 0x07 /* Display control */
+#define SSD1289_FCYCCTRL 0x0b /* Frame cycle control */
+#define SSD1289_PWRCTRL2 0x0c /* Power control 2 */
+#define SSD1289_PWRCTRL3 0x0d /* Power control 3 */
+#define SSD1289_PWRCTRL4 0x0e /* Power control 4 */
+#define SSD1289_GSTART 0x0f /* Gate scan start position */
+#define SSD1289_SLEEP 0x10 /* Sleep mode */
+#define SSD1289_ENTRY 0x11 /* Entry mode */
+#define SSD1289_OPT3 0x12 /* Optimize Access Speed 3 */
+#define SSD1289_GIFCTRL 0x15 /* Generic Interface Control */
+#define SSD1289_HPORCH 0x16 /* Horizontal Porch */
+#define SSD1289_VPORCH 0x17 /* Vertical Porch */
+#define SSD1289_PWRCTRL5 0x1e /* Power control 5 */
+#define SSD1289_DATA 0x22 /* RAM data/write data */
+#define SSD1289_WRMASK1 0x23 /* RAM write data mask 1 */
+#define SSD1289_WRMASK2 0x24 /* RAM write data mask 2 */
+#define SSD1289_FFREQ 0x25 /* Frame Frequency */
+#define SSD1289_VCOMOTP1 0x28 /* VCOM OTP */
+#define SSD1289_OPT1 0x28 /* Optimize Access Speed 1 */
+#define SSD1289_VCOMOTP2 0x29 /* VCOM OTP */
+#define SSD1289_OPT2 0x2f /* Optimize Access Speed 2 */
+#define SSD1289_GAMMA1 0x30 /* Gamma control 1 */
+#define SSD1289_GAMMA2 0x31 /* Gamma control 2 */
+#define SSD1289_GAMMA3 0x32 /* Gamma control 3 */
+#define SSD1289_GAMMA4 0x33 /* Gamma control 4 */
+#define SSD1289_GAMMA5 0x34 /* Gamma control 5 */
+#define SSD1289_GAMMA6 0x35 /* Gamma control 6 */
+#define SSD1289_GAMMA7 0x36 /* Gamma control 7 */
+#define SSD1289_GAMMA8 0x37 /* Gamma control 8 */
+#define SSD1289_GAMMA9 0x3a /* Gamma control 9 */
+#define SSD1289_GAMMA10 0x3b /* Gamma control 10 */
+#define SSD1289_VSCROLL1 0x41 /* Vertical scroll control 1 */
+#define SSD1289_VSCROLL2 0x42 /* Vertical scroll control 2 */
+#define SSD1289_HADDR 0x44 /* Horizontal RAM address position */
+#define SSD1289_VSTART 0x45 /* Vertical RAM address start position */
+#define SSD1289_VEND 0x46 /* Vertical RAM address end position */
+#define SSD1289_W1START 0x48 /* First window start */
+#define SSD1289_W1END 0x49 /* First window end */
+#define SSD1289_W2START 0x4a /* Second window start */
+#define SSD1289_W2END 0x4b /* Second window end */
+#define SSD1289_XADDR 0x4e /* Set GDDRAM X address counter */
+#define SSD1289_YADDR 0x4f /* Set GDDRAM Y address counter */
+
+/* SSD1289 Register Bit definitions */
+
+/* Index register (DC=0) */
+
+#define SSD1289_INDEX_MASK 0xff
+
+/* Device code (read) */
+
+#define SSD1289_DEVCODE_VALUE 0x8989
+
+/* Oscillation Start (write) */
+
+#define SSD1289_OSCSTART_OSCEN (1 << 0) /* Enable oscillator */
+
+/* Driver output control */
+
+#define SSD1289_OUTCTRL_MUX_SHIFT (0) /* Number of lines for the LCD driver */
+#define SSD1289_OUTCTRL_MUX_MASK (0x1ff << SSD1289_OUTCTRL_MUX_SHIFT)
+# define SSD1289_OUTCTRL_MUX(n) ((n) << SSD1289_OUTCTRL_MUX_SHIFT)
+#define SSD1289_OUTCTRL_TB (1 << 9) /* Selects the output shift direction of the gate driver */
+#define SSD1289_OUTCTRL_SM (1 << 10) /* Scanning order of gate driver */
+#define SSD1289_OUTCTRL_BGR (1 << 11) /* Order from RGB to BGR in 18-bit GDDRAM data */
+#define SSD1289_OUTCTRL_CAD (1 << 12) /* Retention capacitor configuration of the TFT panel */
+#define SSD1289_OUTCTRL_REV (1 << 13) /* Reversed display */
+#define SSD1289_OUTCTRL_RL (1 << 14) /* RL pin state */
+
+/* LCD drive AC control */
+
+#define SSD1289_ACCTRL_NW_SHIFT (0) /* Number of lines to alternate in N-line inversion */
+#define SSD1289_ACCTRL_NW_MASK (0xff << SSD1289_ACCTRL_NW_SHIFT)
+#define SSD1289_ACCTRL_WSMD (1 << 8) /* Waveform of WSYNC output */
+#define SSD1289_ACCTRL_EOR (1 << 9) /* EOR signals */
+#define SSD1289_ACCTRL_BC (1 << 10) /* Select the liquid crystal drive waveform */
+#define SSD1289_ACCTRL_ENWS (1 << 11) /* Enables WSYNC output pin */
+#define SSD1289_ACCTRL_FLD (1 << 12) /* Set display in interlace drive mode */
+
+/* Power control 1 */
+
+#define SSD1289_PWRCTRL1_AP_SHIFT (1) /* Current from internal operational amplifier */
+#define SSD1289_PWRCTRL1_AP_MASK (7 << SSD1289_PWRCTRL1_AP_SHIFT)
+# define SSD1289_PWRCTRL1_AP_LEAST (0 << SSD1289_PWRCTRL1_AP_SHIFT)
+# define SSD1289_PWRCTRL1_AP_SMALL (1 << SSD1289_PWRCTRL1_AP_SHIFT)
+# define SSD1289_PWRCTRL1_AP_SMMED (2 << SSD1289_PWRCTRL1_AP_SHIFT)
+# define SSD1289_PWRCTRL1_AP_MEDIUM (3 << SSD1289_PWRCTRL1_AP_SHIFT)
+# define SSD1289_PWRCTRL1_AP_MEDLG (4 << SSD1289_PWRCTRL1_AP_SHIFT)
+# define SSD1289_PWRCTRL1_AP_LARGE (5 << SSD1289_PWRCTRL1_AP_SHIFT)
+# define SSD1289_PWRCTRL1_AP_LGMX (6 << SSD1289_PWRCTRL1_AP_SHIFT)
+# define SSD1289_PWRCTRL1_AP_MAX (7 << SSD1289_PWRCTRL1_AP_SHIFT)
+#define SSD1289_PWRCTRL1_DC_SHIFT (4) /* Set the step-up cycle of the step-up circuit for 262k-color mode */
+#define SSD1289_PWRCTRL1_DC_MASK (15 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FLINEx24 (0 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FLINEx16 (1 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FLINEx12 (2 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FLINEx8 (3 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FLINEx6 (4 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FLINEx5 (5 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FLINEx4 (6 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FLINEx3 (7 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FLINEx2 (8 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FLINEx1 (9 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FOSd4 (10 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FOSd6 (11 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FOSd8 (12 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FOSd10 (13 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FOSd12 (14 << SSD1289_PWRCTRL1_DC_SHIFT)
+# define SSD1289_PWRCTRL1_DC_FOSd16 (15 << SSD1289_PWRCTRL1_DC_SHIFT)
+#define SSD1289_PWRCTRL1_BT_SHIFT (9) /* Control the step-up factor of the step-up circuit */
+#define SSD1289_PWRCTRL1_BT_MASK (7 << SSD1289_PWRCTRL1_BT_SHIFT)
+# define SSD1289_PWRCTRL1_BT_p6m5 (0 << SSD1289_PWRCTRL1_BT_SHIFT)
+# define SSD1289_PWRCTRL1_BT_p6m4 (1 << SSD1289_PWRCTRL1_BT_SHIFT)
+# define SSD1289_PWRCTRL1_BT_p6m6 (2 << SSD1289_PWRCTRL1_BT_SHIFT)
+# define SSD1289_PWRCTRL1_BT_p5m5 (3 << SSD1289_PWRCTRL1_BT_SHIFT)
+# define SSD1289_PWRCTRL1_BT_p5m4 (4 << SSD1289_PWRCTRL1_BT_SHIFT)
+# define SSD1289_PWRCTRL1_BT_p5m3 (5 << SSD1289_PWRCTRL1_BT_SHIFT)
+# define SSD1289_PWRCTRL1_BT_p4m4 (6 << SSD1289_PWRCTRL1_BT_SHIFT)
+# define SSD1289_PWRCTRL1_BT_p4m3 (7 << SSD1289_PWRCTRL1_BT_SHIFT)
+#define SSD1289_PWRCTRL1_DCT_SHIFT (12) /* Step-up cycle of the step-up circuit for 8-color mode */
+#define SSD1289_PWRCTRL1_DCT_MASK (15 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FLINEx24 (0 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FLINEx16 (1 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FLINEx12 (2 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FLINEx8 (3 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FLINEx6 (4 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FLINEx5 (5 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FLINEx4 (6 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FLINEx3 (7 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FLINEx2 (8 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FLINEx1 (9 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FOSd4 (10 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FOSd6 (11 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FOSd8 (12 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FOSd10 (13 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FOSd12 (14 << SSD1289_PWRCTRL1_DCT_SHIFT)
+# define SSD1289_PWRCTRL1_DCT_FOSd16 (15 << SSD1289_PWRCTRL1_DCT_SHIFT)
+
+/* Compare register 1 and 2 */
+
+#define SSD1289_CMP1_CPG_SHIFT (2)
+#define SSD1289_CMP1_CPG_MASK (0x3f << SSD1289_CMP1_CPG_SHIFT)
+#define SSD1289_CMP1_CPR_SHIFT (10)
+#define SSD1289_CMP1_CPR_MASK (0x3f << SSD1289_CMP1_CPR_SHIFT)
+
+#define SSD1289_CMP2_CPB_SHIFT (2)
+#define SSD1289_CMP2_CPB_MASK (0x3f << SSD1289_CMP2_CPB_SHIFT)
+
+/* Display control */
+
+#define SSD1289_DSPCTRL_D_SHIFT (0) /* Display control */
+#define SSD1289_DSPCTRL_D_MASK (3 << SSD1289_DSPCTRL_D_SHIFT)
+# define SSD1289_DSPCTRL_OFF (0 << SSD1289_DSPCTRL_D_SHIFT)
+# define SSD1289_DSPCTRL_INTERNAL (1 << SSD1289_DSPCTRL_D_SHIFT)
+# define SSD1289_DSPCTRL_ON (3 << SSD1289_DSPCTRL_D_SHIFT)
+#define SSD1289_DSPCTRL_CM (1 << 3) /* 8-color mode setting */
+#define SSD1289_DSPCTRL_DTE (1 << 4) /* Selected gate level */
+#define SSD1289_DSPCTRL_GON (1 << 5) /* Gate off level */
+#define SSD1289_DSPCTRL_SPT (1 << 8) /* 2-division LCD drive */
+#define SSD1289_DSPCTRL_VLE_SHIFT (9) /* Vertical scroll control */
+#define SSD1289_DSPCTRL_VLE_MASK (3 << SSD1289_DSPCTRL_VLE_SHIFT)
+# define SSD1289_DSPCTRL_VLE(n) ((n) << SSD1289_DSPCTRL_VLE_SHIFT)
+#define SSD1289_DSPCTRL_PT_SHIFT (11) /* Normalize the source outputs */
+#define SSD1289_DSPCTRL_PT_MASK (3 << SSD1289_DSPCTRL_PT_SHIFT)
+# define SSD1289_DSPCTRL_PT(n) ((n) << SSD1289_DSPCTRL_PT_SHIFT)
+
+/* Frame cycle control */
+
+#define SSD1289_FCYCCTRL_RTN_SHIFT (0) /* Number of clocks in each line */
+#define SSD1289_FCYCCTRL_RTN_MASK (3 << SSD1289_FCYCCTRL_RTN_SHIFT)
+# define SSD1289_FCYCCTRL_RTN(n) (((n)-16) << SSD1289_FCYCCTRL_RTN_SHIFT)
+#define SSD1289_FCYCCTRL_SRTN (1 << 4) /* When SRTN =1, RTN3-0 value will be count */
+#define SSD1289_FCYCCTRL_SDIV (1 << 5) /* When SDIV = 1, DIV1-0 value will be count */
+#define SSD1289_FCYCCTRL_DIV_SHIFT (6) /* Set the division ratio of clocks */
+#define SSD1289_FCYCCTRL_DIV_MASK (3 << SSD1289_FCYCCTRL_DIV_SHIFT)
+# define SSD1289_FCYCCTRL_DIV1 (0 << SSD1289_FCYCCTRL_DIV_SHIFT)
+# define SSD1289_FCYCCTRL_DIV2 (1 << SSD1289_FCYCCTRL_DIV_SHIFT)
+# define SSD1289_FCYCCTRL_DIV4 (2 << SSD1289_FCYCCTRL_DIV_SHIFT)
+# define SSD1289_FCYCCTRL_DIV8 (3 << SSD1289_FCYCCTRL_DIV_SHIFT)
+#define SSD1289_FCYCCTRL_EQ_SHIFT (8) /* Sets the equalizing period */
+#define SSD1289_FCYCCTRL_EQ_MASK (3 << SSD1289_FCYCCTRL_EQ_SHIFT)
+# define SSD1289_FCYCCTRL_EQ(n) (((n)-1) << SSD1289_FCYCCTRL_EQ_SHIFT) /* n = 2-8 clocks */
+#define SSD1289_FCYCCTRL_SDT_SHIFT (12) /* Set delay amount from the gate output */
+#define SSD1289_FCYCCTRL_SDT_MASK (3 << SSD1289_FCYCCTRL_SDT_SHIFT)
+# define SSD1289_FCYCCTRL_SDT(n) ((n) << SSD1289_FCYCCTRL_SDT_SHIFT) /* n = 1-3 clocks */
+#define SSD1289_FCYCCTRL_NO_SHIFT (14) /* Sets amount of non-overlap of the gate output */
+#define SSD1289_FCYCCTRL_NO_MASK (3 << SSD1289_FCYCCTRL_NO_SHIFT)
+# define SSD1289_FCYCCTRL_NO(n) ((n) << SSD1289_FCYCCTRL_NO_SHIFT) /* n = 1-3 clocks */
+
+/* Power control 2 */
+
+#define SSD1289_PWRCTRL2_VRC_SHIFT (0) /* Adjust VCIX2 output voltage */
+#define SSD1289_PWRCTRL2_VRC_MASK (7 << SSD1289_PWRCTRL2_VRC_SHIFT)
+# define SSD1289_PWRCTRL2_VRC_5p1V (0 << SSD1289_PWRCTRL2_VRC_SHIFT)
+# define SSD1289_PWRCTRL2_VRC_5p2V (1 << SSD1289_PWRCTRL2_VRC_SHIFT)
+# define SSD1289_PWRCTRL2_VRC_5p3V (2 << SSD1289_PWRCTRL2_VRC_SHIFT)
+# define SSD1289_PWRCTRL2_VRC_5p4V (3 << SSD1289_PWRCTRL2_VRC_SHIFT)
+# define SSD1289_PWRCTRL2_VRC_5p5V (4 << SSD1289_PWRCTRL2_VRC_SHIFT)
+# define SSD1289_PWRCTRL2_VRC_5p6V (5 << SSD1289_PWRCTRL2_VRC_SHIFT)
+# define SSD1289_PWRCTRL2_VRC_5p7V (6 << SSD1289_PWRCTRL2_VRC_SHIFT)
+# define SSD1289_PWRCTRL2_VRC_5p8V (7 << SSD1289_PWRCTRL2_VRC_SHIFT)
+
+/* Power control 3 */
+
+#define SSD1289_PWRCTRL3_VRH_SHIFT (0) /* Set amplitude magnification of VLCD63 */
+#define SSD1289_PWRCTRL3_VRH_MASK (15 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x1p540 (0 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x1p620 (1 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x1p700 (2 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x1p780 (3 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x1p850 (4 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x1p930 (5 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x2p020 (6 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x2p090 (7 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x2p165 (8 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x2p245 (9 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x2p335 (10 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x2p400 (11 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x2p500 (12 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x2p570 (13 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x2p645 (14 << SSD1289_PWRCTRL3_VRH_SHIFT)
+# define SSD1289_PWRCTRL3_VRH_x2p725 (15 << SSD1289_PWRCTRL3_VRH_SHIFT)
+
+/* Power control 4 */
+
+#define SSD1289_PWRCTRL4_VDV_SHIFT (8) /* Set amplitude magnification of VLCD63 */
+#define SSD1289_PWRCTRL4_VDV_MASK (32 << SSD1289_PWRCTRL4_VDV_SHIFT)
+# define SSD1289_PWRCTRL4_VDV(n) ((n) << SSD1289_PWRCTRL4_VDV_SHIFT)
+#define SSD1289_PWRCTRL4_VCOMG (1 << 13) /* VcomL variable */
+
+/* Gate scan start position */
+
+#define SSD1289_GSTART_MASK 0x1ff
+
+/* Sleep mode */
+
+#define SSD1289_SLEEP_ON (1 << 0)
+
+/* Entry mode */
+
+#define SSD1289_ENTRY_LG_SHIFT (0) /* Write after comparing */
+#define SSD1289_ENTRY_LG_MASK (7 << SSD1289_ENTRY_LG_SHIFT)
+#define SSD1289_ENTRY_AM (1 << 3) /* Address counter direction */
+#define SSD1289_ENTRY_ID_SHIFT (4) /* Address increment mode */
+#define SSD1289_ENTRY_ID_MASK (3 << SSD1289_ENTRY_ID_SHIFT)
+# define SSD1289_ENTRY_ID_HDECVDEC (0 << SSD1289_ENTRY_ID_SHIFT)
+# define SSD1289_ENTRY_ID_HINCVDEC (1 << SSD1289_ENTRY_ID_SHIFT)
+# define SSD1289_ENTRY_ID_HDECVINC (2 << SSD1289_ENTRY_ID_SHIFT)
+# define SSD1289_ENTRY_ID_HINCVINC (3 << SSD1289_ENTRY_ID_SHIFT)
+#define SSD1289_ENTRY_TY_SHIFT (6) /* RAM data write method */
+#define SSD1289_ENTRY_TY_MASK (3 << SSD1289_ENTRY_TY_SHIFT)
+# define SSD1289_ENTRY_TY_A (0 << SSD1289_ENTRY_TY_SHIFT)
+# define SSD1289_ENTRY_TY_B (1 << SSD1289_ENTRY_TY_SHIFT)
+# define SSD1289_ENTRY_TY_C (2 << SSD1289_ENTRY_TY_SHIFT)
+#define SSD1289_ENTRY_DMODE_SHIFT (8) /* Data display mode */
+#define SSD1289_ENTRY_DMODE_MASK (3 << SSD1289_ENTRY_DMODE_SHIFT)
+# define SSD1289_ENTRY_DMODE_RAM (0 << SSD1289_ENTRY_DMODE_SHIFT)
+# define SSD1289_ENTRY_DMODE_GENERIC (1 << SSD1289_ENTRY_DMODE_SHIFT)
+# define SSD1289_ENTRY_DMODE_RAMGEN (2 << SSD1289_ENTRY_DMODE_SHIFT)
+# define SSD1289_ENTRY_DMODE_GENRAM (3 << SSD1289_ENTRY_DMODE_SHIFT)
+#define SSD1289_ENTRY_WMODE (1 << 10) /* Select source of data in RAM */
+#define SSD1289_ENTRY_OEDEF (1 << 11) /* Define display window */
+#define SSD1289_ENTRY_TRANS (1 << 12) /* Transparent display */
+#define SSD1289_ENTRY_DFM_SHIFT (13) /* Color display mode */
+#define SSD1289_ENTRY_DFM_MASK (3 << SSD1289_ENTRY_DFM_SHIFT)
+# define SSD1289_ENTRY_DFM_262K (2 << SSD1289_ENTRY_DFM_SHIFT)
+# define SSD1289_ENTRY_DFM_65K (3 << SSD1289_ENTRY_DFM_SHIFT)
+#define SSD1289_ENTRY_VSMODE (1 << 15) /* Frame frequency depends on VSYNC */
+
+/* Generic Interface Control */
+
+#define SSD1289_GIFCTRL_INVVS (1 << 0) /* Sets the signal polarity of DOTCLK pin */
+#define SSD1289_GIFCTRL_INVHS (1 << 1) /* Sets the signal polarity of DEN pin */
+#define SSD1289_GIFCTRL_NVDEN (1 << 2) /* Sets the signal polarity of HSYNC pin */
+#define SSD1289_GIFCTRL_INVDOT (1 << 3) /* Sets the signal polarity of VSYNC pin */
+
+/* Horizontal Porch */
+
+#define SSD1289_HPORCH_HBP_SHIFT (0) /* Set delay from falling edge of HSYNC signal to data */
+#define SSD1289_HPORCH_HBP_MASK (0xff << SSD1289_HPORCH_HBP_SHIFT)
+#define SSD1289_HPORCH_XL_SHIFT (8) /* number of valid pixel per line */
+#define SSD1289_HPORCH_XL_MASK (0xff << SSD1289_HPORCH_XL_SHIFT)
+
+/* Vertical Porch */
+
+#define SSD1289_VPORCH_VBP_SHIFT (0) /* Set delay from falling edge of VSYNC signal to line */
+#define SSD1289_VPORCH_VBP_MASK (0xff << SSD1289_VPORCH_VBP_SHIFT)
+#define SSD1289_VPORCH_XFP_SHIFT (8) /* Delay from last line to falling edge of VSYNC of next frame */
+#define SSD1289_VPORCH_XFP_MASK (0xff << SSD1289_VPORCH_XFP_SHIFT)
+#define SSD1289_VPORCH_
+
+/* Power control 5 */
+
+#define SSD1289_PWRCTRL5_VCM_SHIFT (0) /* Set the VcomH voltage */
+#define SSD1289_PWRCTRL5_VCM_MASK (0x3f << SSD1289_PWRCTRL5_VCM_SHIFT)
+# define SSD1289_PWRCTRL5_VCM(n) ((n) << SSD1289_PWRCTRL5_VCM_SHIFT)
+#define SSD1289_PWRCTRL5_NOTP (1 << 7) /* 1=VCM valid */
+
+/* RAM write data mask 1 */
+
+#define SSD1289_WRMASK1_WMG_SHIFT (2)
+#define SSD1289_WRMASK1_WMG_MASK (0x3f << SSD1289_WRMASK1_WMG_SHIFT)
+#define SSD1289_WRMASK1_WMR_SHIFT (10)
+#define SSD1289_WRMASK1_WMR_MASK (0x3f << SSD1289_WRMASK1_WMR_SHIFT)
+
+#define SSD1289_WRMASK2_WMB_SHIFT (2)
+#define SSD1289_WRMASK2_WMB_MASK (0x3f << SSD1289_WRMASK2_WMB_SHIFT)
+
+/* Frame Frequency */
+
+#define SSD1289_FFREQ_OSC_SHIFT (12) /* Set the frame frequency */
+#define SSD1289_FFREQ_OSC_MASK (15 << SSD1289_FFREQ_OSC_SHIFT)
+# define SSD1289_FFREQ_OSC_FF50 (0 << SSD1289_FFREQ_OSC_SHIFT)
+# define SSD1289_FFREQ_OSC_FF55 (2 << SSD1289_FFREQ_OSC_SHIFT)
+# define SSD1289_FFREQ_OSC_FF60 (5 << SSD1289_FFREQ_OSC_SHIFT)
+# define SSD1289_FFREQ_OSC_FF65 (8 << SSD1289_FFREQ_OSC_SHIFT)
+# define SSD1289_FFREQ_OSC_FF70 (10 << SSD1289_FFREQ_OSC_SHIFT)
+# define SSD1289_FFREQ_OSC_FF75 (12 << SSD1289_FFREQ_OSC_SHIFT)
+# define SSD1289_FFREQ_OSC_FF80 (14 << SSD1289_FFREQ_OSC_SHIFT)
+
+/* VCOM OTP */
+
+#define SSD1289_VCOMOTP1_ACTIVATE 0x0006
+#define SSD1289_VCOMOTP1_FIRE 0x000a
+#define SSD1289_VCOMOTP2_ACTIVATE 0x80c0
+
+/* Optimize Access Speed 1, 2, 3 (omitted) */
+
+/* Gamma control 1-10. Magic values. I won't try to represent the fields. */
+
+/* Vertical scroll control 1 and 2 */
+
+#define SSD1289_VSCROLL_MASK 0x1ff /* Scroll length */
+
+/* Horizontal RAM address position */
+
+#define SSD1289_HADDR_HSA_SHIFT (0) /* Window horizontal start address */
+#define SSD1289_HADDR_HSA_MASK (0xff << SSD1289_HADDR_HSA_SHIFT)
+#define SSD1289_HADDR_HEA_SHIFT (8) /* Window horizontal end address */
+#define SSD1289_HADDR_HEA_MASK (0xff << SSD1289_HADDR_HEA_SHIFT)
+
+/* Vertical RAM address start/end position */
+
+#define SSD1289_VSTART_MASK 0x1ff /* Window Vertical start address */
+#define SSD1289_VEND_MASK 0x1ff /* Window Vertical end address */
+
+/* First window start/end */
+
+#define SSD1289_W1START_MASK 0x1ff /* Start line for first screen */
+#define SSD1289_W1END_MASK 0x1ff /* End line for first screen */
+
+/* Second window start/end */
+
+#define SSD1289_W2START_MASK 0x1ff /* Start line for second screen */
+#define SSD1289_W2END_MASK 0x1ff /* End line for second screen */
+
+/* Set GDDRAM X/Y address counter */
+
+#define SSD1289_XADDR_MASK 0xff /* GDDRAM X address in the address counter */
+#define SSD1289_YADDR_MASK 0x1ff /* GDDRAM Y address in the address counter */
+
+#endif /* CONFIG_LCD_SSD1289 */
+#endif /* __DRIVERS_LCD_SSD1289_H */
diff --git a/nuttx/drivers/lcd/ssd1305.h b/nuttx/drivers/lcd/ssd1305.h
new file mode 100644
index 000000000..34678fa80
--- /dev/null
+++ b/nuttx/drivers/lcd/ssd1305.h
@@ -0,0 +1,211 @@
+/**************************************************************************************
+ * drivers/lcd/ssd1305.h
+ * Definitions for the Solomon Systech SSD1305 132x64 Dot Matrix OLED/PLED
+ * Segment/Common Driver with C
+ *
+ * Copyright (C) 2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: SSD1305.pdf, "Solomon Systech SSD1305 132x64 Dot Matrix OLED/PLED
+ * Segment/Common Driver with Controller," Solomon Systech Limited,
+ * http://www.solomon-systech.com, May, 2008.
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ **************************************************************************************/
+
+#ifndef __DRIVERS_LCD_SSD1305_H
+#define __DRIVERS_LCD_SSD1305_H
+
+/**************************************************************************************
+ * Included Files
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Pre-processor Definitions
+ **************************************************************************************/
+/* General Definitions ******************************************************/
+
+#define SSD1305_COLORA 0
+#define SSD1305_COLORB 1
+#define SSD1305_COLORC 2
+#define SSD1305_COLORD 3
+
+/* Fundamental Commands *****************************************************/
+#define SSD1305_SETCOLL 0x00 /* 0x00-0x0f: Set lower column address */
+# define SSD1305_COLL_MASK 0x0f
+#define SSD1305_SETCOLH 0x10 /* 0x10-0x1f: Set higher column address */
+# define SSD1305_COLH_MASK 0x0f
+#define SSD1305_ADDRMODE 0x20 /* 0x20: Set memory address mode */
+# define SSD1305_ADDRMODE_HOR 0x00 /* Data 1: Set horizontal address mode */
+# define SSD1305_ADDRMODE_VIRT 0x01 /* Data 1: Set virtal address mode */
+# define SSD1305_ADDRMODE_PAGE 0x02 /* Data 1: Set page address mode */
+#define SSD1305_SETCOLADDR 0x21 /* 0x21: Set column address */
+ /* Data 1: Column start address: 0-131 */
+ /* Data 2: Column end address: 0-131 */
+#define SSD1305_SETPAGEADDR 0x22 /* 0x22: Set page address */
+ /* Data 1: Page start address: 0x00-0x7d */
+ /* Data 2: Page end address: 0x00-0x7d */
+#define SSD1305_SETSTARTLINE 0x40 /* 0x40-7f: Set display start line */
+# define SSD1305_STARTLINE_MASK 0x3f
+
+#define SSD1305_SETCONTRAST 0x81 /* 0x81: Set contrast control */
+ /* Data 1: Set 1 of 256 contrast steps */
+#define SSD1305_SETBRIGHTNESS 0x82 /* 0x82: Set brightness */
+ /* Data 1: Set 1 of 256 contrast steps */
+#define SSD1305_SETLUT 0x91 /* 0x01: Set lookup table */
+ /* Data 1: Pulse width: 31-63 */
+ /* Data 2: Color A: 31-63 */
+ /* Data 3: Color B: 31-63 */
+ /* Data 4: Color C: 31-63 */
+#define SSD1305_SETBANKCOLOR1 0x92 /* 0x92: Set bank 1-16 color */
+# define SSD1305_SETBANK1(c) (c) /* Data 1, Bits 0-1: Bank 1 color */
+# define SSD1305_SETBANK2(c) (c << 2) /* Data 1, Bits 2-3: Bank 2 color */
+# define SSD1305_SETBANK3(c) (c << 4) /* Data 1, Bits 4-5: Bank 3 color */
+# define SSD1305_SETBANK4(c) (c << 6) /* Data 1, Bits 6-7: Bank 4 color */
+# define SSD1305_SETBANK5(c) (c) /* Data 2, Bits 0-1: Bank 5 color */
+# define SSD1305_SETBANK6(c) (c << 2) /* Data 2, Bits 2-3: Bank 6 color */
+# define SSD1305_SETBANK7(c) (c << 4) /* Data 2, Bits 4-5: Bank 7 color */
+# define SSD1305_SETBANK8(c) (c << 6) /* Data 2, Bits 6-7: Bank 8 color */
+# define SSD1305_SETBANK9(c) (c) /* Data 3, Bits 0-1: Bank 9 color */
+# define SSD1305_SETBANK10(c) (c << 2) /* Data 3, Bits 2-3: Bank 10 color */
+# define SSD1305_SETBANK11(c) (c << 4) /* Data 3, Bits 4-5: Bank 11 color */
+# define SSD1305_SETBANK12(c) (c << 6) /* Data 3, Bits 6-7: Bank 12 color */
+# define SSD1305_SETBANK13(c) (c) /* Data 4, Bits 0-1: Bank 13 color */
+# define SSD1305_SETBANK14(c) (c << 2) /* Data 4, Bits 2-3: Bank 14 color */
+# define SSD1305_SETBANK15(c) (c << 4) /* Data 4, Bits 4-5: Bank 15 color */
+# define SSD1305_SETBANK16(c) (c << 6) /* Data 4, Bits 6-7: Bank 16 color */
+#define SSD1305_SETBANKCOLOR2 0x93 /* 0x93: Set bank 17-32 color */
+# define SSD1305_SETBANK17(c) (c) /* Data 1, Bits 0-1: Bank 17 color */
+# define SSD1305_SETBANK18(c) (c << 2) /* Data 1, Bits 2-3: Bank 18 color */
+# define SSD1305_SETBANK19(c) (c << 4) /* Data 1, Bits 4-5: Bank 19 color */
+# define SSD1305_SETBANK20(c) (c << 6) /* Data 1, Bits 6-7: Bank 20 color */
+# define SSD1305_SETBANK21(c) (c) /* Data 2, Bits 0-1: Bank 21 color */
+# define SSD1305_SETBANK22(c) (c << 2) /* Data 2, Bits 2-3: Bank 22 color */
+# define SSD1305_SETBANK23(c) (c << 4) /* Data 2, Bits 4-5: Bank 23 color */
+# define SSD1305_SETBANK24(c) (c << 6) /* Data 2, Bits 6-7: Bank 24 color */
+# define SSD1305_SETBANK25(c) (c) /* Data 3, Bits 0-1: Bank 25 color */
+# define SSD1305_SETBANK26(c) (c << 2) /* Data 3, Bits 2-3: Bank 26 color */
+# define SSD1305_SETBANK27(c) (c << 4) /* Data 3, Bits 4-5: Bank 27 color */
+# define SSD1305_SETBANK28(c) (c << 6) /* Data 3, Bits 6-7: Bank 28 color */
+# define SSD1305_SETBANK29(c) (c) /* Data 4, Bits 0-1: Bank 29 color */
+# define SSD1305_SETBANK30(c) (c << 2) /* Data 4, Bits 2-3: Bank 30 color */
+# define SSD1305_SETBANK31(c) (c << 4) /* Data 4, Bits 4-5: Bank 31 color */
+# define SSD1305_SETBANK32(c) (c << 6) /* Data 4, Bits 6-7: Bank 32 color */
+#define SSD1305_MAPCOL0 0xa0 /* 0xa0: Column address 0 is mapped to SEG0 */
+#define SSD1305_MAPCOL131 0xa1 /* 0xa1: Column address 131 is mapped to SEG0 */
+#define SSD1305_DISPRAM 0xa4 /* 0xa4: Resume to RAM content display */
+#define SSD1305_DISPENTIRE 0xa5 /* 0xa5: Entire display ON */
+#define SSD1305_DISPNORMAL 0xa6 /* 0xa6: Normal display */
+#define SSD1305_DISPINVERTED 0xa7 /* 0xa7: Inverse display */
+
+#define SSD1305_SETMUX 0xa8 /* 0xa8: Set Multiplex Ratio*/
+ /* Data 1: MUX ratio -1: 15-63 */
+#define SSD1305_DIMMODE 0xab /* 0xab: Dim mode setting */
+ /* Data 1: Reserverd, must be zero */
+ /* Data 2: Contrast for bank1: 0-255 */
+ /* Data 3: Brightness for color bank: 0-255 */
+#define SSD1305_MSTRCONFIG 0xad /* 0xad: Master configuration */
+# define SSD1305_MSTRCONFIG_EXTVCC 0x8e /* Data 1: Select external Vcc */
+#define SSD1305_DISPONDIM 0xac /* 0xac: Display ON in dim mode */
+#define SSD1305_DISPOFF 0xae /* 0xae: Display OFF (sleep mode) */
+#define SSD1305_DISPON 0xaf /* 0xaf: Display ON in normal mode */
+#define SSD1305_SETPAGESTART 0xb0 /* 0xb0-b7: Set page start address */
+# define SSD1305_PAGESTART_MASK 0x07
+#define SSD1305_SETCOMNORMAL 0xc0 /* 0xc0: Set COM output, normal mode */
+#define SSD1305_SETCOMREMAPPED 0xc8 /* 0xc8: Set COM output, remapped mode */
+
+#define SSD1305_SETOFFSET 0xd3 /* 0xd3: Set display offset */
+ /* Data 1: Vertical shift by COM: 0-63 */
+#define SSD1305_SETDCLK 0xd5 /* 0xd5: Set display clock divide ratio/oscillator */
+# define SSD1305_DCLKDIV_SHIFT (0) /* Data 1, Bits 0-3: DCLK divide ratio/frequency*/
+# define SSD1305_DCLKDIV_MASK 0x0f
+# define SSD1305_DCLKFREQ_SHIFT (4) /* Data 1, Bits 4-7: DCLK divide oscillator frequency */
+# define SSD1305_DCLKFREQ_MASK 0xf0
+#define SSD1305_SETCOLORMODE 0xd8 /* 0xd: Set area color and low power display modes */
+# define SSD1305_COLORMODE_MONO 0x00 /* Data 1, Bits 4-5: 00=monochrome */
+# define SSD1305_COLORMODE_COLOR 0x30 /* Data 1, Bits 4-5: 11=area color enable */
+# define SSD1305_POWERMODE_NORMAL 0x00 /* Data 1, Bits 0,2: 00=normal power mode */
+# define SSD1305_POWERMODE_LOW 0x05 /* Data 1, Bits 0,2: 11=low power display mode */
+#define SSD1305_SETPRECHARGE 0xd9 /* 0xd9: Set pre-charge period */
+# define SSD1305_PHASE1_SHIFT (0) /* Data 1, Bits 0-3: Phase 1 period of up to 15 DCLK clocks */
+# define SSD1305_PHASE1_MASK 0x0f
+# define SSD1305_PHASE2_SHIFT (4) /* Data 1, Bits 4-7: Phase 2 period of up to 15 DCLK clocks */
+# define SSD1305_PHASE2_MASK 0xf0
+#define SSD1305_SETCOMCONFIG 0xda /* 0xda: Set COM configuration */
+# define SSD1305_COMCONFIG_SEQ 0x02 /* Data 1, Bit 4: 0=Sequential COM pin configuration */
+# define SSD1305_COMCONFIG_ALT 0x12 /* Data 1, Bit 4: 1=Alternative COM pin configuration */
+# define SSD1305_COMCONFIG_NOREMAP 0x02 /* Data 1, Bit 5: 0=Disable COM Left/Right remap */
+# define SSD1305_COMCONFIG_REMAP 0x22 /* Data 1, Bit 5: 1=Enable COM Left/Right remap */
+#define SSD1305_SETVCOMHDESEL 0xdb /* 0xdb: Set VCOMH delselect level */
+# define SSD1305_VCOMH_x4p3 0x00 /* Data 1: ~0.43 x Vcc */
+# define SSD1305_VCOMH_x7p7 0x34 /* Data 1: ~0.77 x Vcc */
+# define SSD1305_VCOMH_x8p3 0x3c /* Data 1: ~0.83 x Vcc */
+#define SSD1305_ENTER_RMWMODE 0xe0 /* 0xe0: Enter the Read Modify Write mode */
+#define SSD1305_NOP 0xe3 /* 0xe3: NOP Command for no operation */
+#define SSD1305_EXIT_RMWMODE 0xee /* 0xee: Leave the Read Modify Write mode */
+
+/* Graphic Acceleration Commands ********************************************/
+
+#define SSD1305_HSCROLL_RIGHT 0x26 /* 0x26: Right horizontal scroll */
+#define SSD1305_HSCROLL_LEFT 0x27 /* 0x27: Left horizontal scroll */
+ /* Data 1, Bits 0-2: Column scroll offset: 0-4 */
+ /* Data 2, Bits 0-2: Start page address: 0-7 */
+#define SSD1305_HSCROLL_FRAMES6 0x00 /* Data 3, Bits 0-2: Timer interval, 000=6 frames */
+#define SSD1305_HSCROLL_FRAMES32 0x01 /* Data 3, Bits 0-2: Timer interval, 001=32 frames */
+#define SSD1305_HSCROLL_FRAMES64 0x02 /* Data 3, Bits 0-2: Timer interval, 010=64 frames */
+#define SSD1305_HSCROLL_FRAMES128 0x03 /* Data 3, Bits 0-2: Timer interval, 011=128 frames */
+#define SSD1305_HSCROLL_FRAMES3 0x04 /* Data 3, Bits 0-2: Timer interval, 100=3 frames */
+#define SSD1305_HSCROLL_FRAMES4 0x05 /* Data 3, Bits 0-2: Timer interval, 101=4 frames */
+#define SSD1305_HSCROLL_FRAMES2 0x06 /* Data 3, Bits 0-2: Timer interval, 110=2 frames */
+ /* Data 4, Bits 0-2: End page address: 0-7 */
+
+#define SSD1305_VSCROLL_RIGHT 0x29 /* 0x26: Vertical and right horizontal scroll */
+#define SSD1305_VSCROLL_LEFT 0x2a /* 0x27: Vertical and left horizontal scroll */
+ /* Data 1, Bits 0-2: Column scroll offset: 0-4 */
+ /* Data 2, Bits 0-2: Start page address: 0-7 */
+#define SSD1305_VSCROLL_FRAMES6 0x00 /* Data 3, Bits 0-2: Timer interval, 000=6 frames */
+#define SSD1305_VSCROLL_FRAMES32 0x01 /* Data 3, Bits 0-2: Timer interval, 001=32 frames */
+#define SSD1305_VSCROLL_FRAMES64 0x02 /* Data 3, Bits 0-2: Timer interval, 010=64 frames */
+#define SSD1305_VSCROLL_FRAMES128 0x03 /* Data 3, Bits 0-2: Timer interval, 011=128 frames */
+#define SSD1305_VSCROLL_FRAMES3 0x04 /* Data 3, Bits 0-2: Timer interval, 100=3 frames */
+#define SSD1305_VSCROLL_FRAMES4 0x05 /* Data 3, Bits 0-2: Timer interval, 101=4 frames */
+#define SSD1305_VSCROLL_FRAMES2 0x06 /* Data 3, Bits 0-2: Timer interval, 110=2 frames */
+ /* Data 4, Bits 0-2: End page address: 0-7 */
+ /* Data 5, Bits 0-5: Vertical scrolling offset: 0-63 */
+#define SSD1305_SCROLL_STOP 0x2e /* 0x2e: Deactivate scroll */
+#define SSD1305_SCROLL_START 0x2f /* 0x2f: Activate scroll */
+#define SSD1305_VSCROLL_AREA 0xa3 /* 0xa3: Set vertical scroll area */
+ /* Data 1: Number of rows in the top fixed area */
+ /* Data 1: Number of rows in the scroll area */
+
+/* Status register bit definitions ******************************************/
+
+#define SSD1305_STATUS_DISPOFF (1 << 6) /* Bit 6: 1=Display off */
+
+#endif /* __DRIVERS_LCD_SSD1305_H */
diff --git a/nuttx/drivers/lcd/ug-9664hswag01.c b/nuttx/drivers/lcd/ug-9664hswag01.c
new file mode 100644
index 000000000..e0e8e8e3a
--- /dev/null
+++ b/nuttx/drivers/lcd/ug-9664hswag01.c
@@ -0,0 +1,1049 @@
+/**************************************************************************************
+ * drivers/lcd/ug-9664hswag01.c
+ * Driver for the Univision UG-9664HSWAG01 Display with the Solomon Systech SSD1305 LCD
+ * controller.
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Reference: "Product Specification, OEL Display Module, UG-9664HSWAG01", Univision
+ * Technology Inc., SAS1-6020-B, January 3, 2008.
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Included Files
+ **************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ug-9664hswag01.h>
+
+#include "ssd1305.h"
+
+/**************************************************************************************
+ * Pre-processor Definitions
+ **************************************************************************************/
+
+/* Configuration **********************************************************************/
+/* UG-9664HSWAG01 Configuration Settings:
+ *
+ * CONFIG_UG9664HSWAG01_SPIMODE - Controls the SPI mode
+ * CONFIG_UG9664HSWAG01_FREQUENCY - Define to use a different bus frequency
+ * CONFIG_UG9664HSWAG01_NINTERFACES - Specifies the number of physical
+ * UG-9664HSWAG01 devices that will be supported. NOTE: At present, this
+ * must be undefined or defined to be 1.
+ * CONFIG_UG9664HSWAG01_POWER
+ * If the hardware supports a controllable OLED a power supply, this
+ * configuration shold be defined. (See ug_power() below).
+ * CONFIG_LCD_UGDEBUG - Enable detailed UG-9664HSWAG01 debug output
+ * (CONFIG_DEBUG and CONFIG_VERBOSE must also be enabled).
+ *
+ * Required LCD driver settings:
+ * CONFIG_LCD_UG9664HSWAG01 - Enable UG-9664HSWAG01 support
+ * CONFIG_LCD_MAXCONTRAST should be 255, but any value >0 and <=255 will be accepted.
+ * CONFIG_LCD_MAXPOWER should be 2: 0=off, 1=dim, 2=normal
+ *
+ * Required SPI driver settings:
+ * CONFIG_SPI_CMDDATA - Include support for cmd/data selection.
+ */
+
+/* Verify that all configuration requirements have been met */
+
+/* The UG-9664HSWAG01 spec says that is supports SPI mode 0,0 only. However, somtimes
+ * you need to tinker with these things.
+ */
+
+#ifndef CONFIG_UG9664HSWAG01_SPIMODE
+# define CONFIG_UG9664HSWAG01_SPIMODE SPIDEV_MODE0
+#endif
+
+/* SPI frequency */
+
+#ifndef CONFIG_UG9664HSWAG01_FREQUENCY
+# define CONFIG_UG9664HSWAG01_FREQUENCY 3500000
+#endif
+
+/* CONFIG_UG9664HSWAG01_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_UG9664HSWAG01_NINTERFACES
+# define CONFIG_UG9664HSWAG01_NINTERFACES 1
+#endif
+
+#if CONFIG_UG9664HSWAG01_NINTERFACES != 1
+# warning "Only a single UG-9664HSWAG01 interface is supported"
+# undef CONFIG_UG9664HSWAG01_NINTERFACES
+# define CONFIG_UG9664HSWAG01_NINTERFACES 1
+#endif
+
+/* Verbose debug must also be enabled to use the extra OLED debug */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_DEBUG_GRAPHICS
+#endif
+
+#ifndef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_LCD_UGDEBUG
+#endif
+
+/* Check contrast selection */
+
+#ifndef CONFIG_LCD_MAXCONTRAST
+# define CONFIG_LCD_MAXCONTRAST 255
+#endif
+
+#if CONFIG_LCD_MAXCONTRAST <= 0 || CONFIG_LCD_MAXCONTRAST > 255
+# error "CONFIG_LCD_MAXCONTRAST exceeds supported maximum"
+#endif
+
+#if CONFIG_LCD_MAXCONTRAST < 255
+# warning "Optimal setting of CONFIG_LCD_MAXCONTRAST is 255"
+#endif
+
+/* Check power setting */
+
+#if !defined(CONFIG_LCD_MAXPOWER)
+# define CONFIG_LCD_MAXPOWER 2
+#endif
+
+#if CONFIG_LCD_MAXPOWER != 2
+# warning "CONFIG_LCD_MAXPOWER should be 2"
+# undef CONFIG_LCD_MAXPOWER
+# define CONFIG_LCD_MAXPOWER 2
+#endif
+
+/* The OLED requires CMD/DATA SPI support */
+
+#ifndef CONFIG_SPI_CMDDATA
+# error "CONFIG_SPI_CMDDATA must be defined in your NuttX configuration"
+#endif
+
+/* Color is 1bpp monochrome with leftmost column contained in bits 0 */
+
+#ifdef CONFIG_NX_DISABLE_1BPP
+# warning "1 bit-per-pixel support needed"
+#endif
+
+/* Color Properties *******************************************************************/
+/* The SSD1305 display controller can handle a resolution of 132x64. The OLED
+ * on the base board is 96x64.
+ */
+
+#define UG_DEV_XRES 132
+#define UG_XOFFSET 18
+
+/* Display Resolution */
+
+#define UG_XRES 96
+#define UG_YRES 64
+
+/* Color depth and format */
+
+#define UG_BPP 1
+#define UG_COLORFMT FB_FMT_Y1
+
+/* Bytes per logical row andactual device row */
+
+#define UG_XSTRIDE (UG_XRES >> 3) /* Pixels arrange "horizontally for user" */
+#define UG_YSTRIDE (UG_YRES >> 3) /* But actual device arrangement is "vertical" */
+
+/* The size of the shadow frame buffer */
+
+#define UG_FBSIZE (UG_XRES * UG_YSTRIDE)
+
+/* Bit helpers */
+
+#define LS_BIT (1 << 0)
+#define MS_BIT (1 << 7)
+
+/* Debug ******************************************************************************/
+
+#ifdef CONFIG_LCD_UGDEBUG
+# define ugdbg(format, arg...) vdbg(format, ##arg)
+#else
+# define ugdbg(x...)
+#endif
+
+/**************************************************************************************
+ * Private Type Definition
+ **************************************************************************************/
+
+/* This structure describes the state of this driver */
+
+struct ug_dev_s
+{
+ /* Publically visible device structure */
+
+ struct lcd_dev_s dev;
+
+ /* Private LCD-specific information follows */
+
+ FAR struct spi_dev_s *spi;
+ uint8_t contrast;
+ uint8_t powered;
+
+ /* The SSD1305 does not support reading from the display memory in SPI mode.
+ * Since there is 1 BPP and access is byte-by-byte, it is necessary to keep
+ * a shadow copy of the framebuffer memory.
+ */
+
+ uint8_t fb[UG_FBSIZE];
+};
+
+/**************************************************************************************
+ * Private Function Protototypes
+ **************************************************************************************/
+
+/* SPI helpers */
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void ug_select(FAR struct spi_dev_s *spi);
+static inline void ug_deselect(FAR struct spi_dev_s *spi);
+#else
+static void ug_select(FAR struct spi_dev_s *spi);
+static void ug_deselect(FAR struct spi_dev_s *spi);
+#endif
+
+/* LCD Data Transfer Methods */
+
+static int ug_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels);
+static int ug_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels);
+
+/* LCD Configuration */
+
+static int ug_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo);
+static int ug_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+# error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+# error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ug_getpower(struct lcd_dev_s *dev);
+static int ug_setpower(struct lcd_dev_s *dev, int power);
+static int ug_getcontrast(struct lcd_dev_s *dev);
+static int ug_setcontrast(struct lcd_dev_s *dev, unsigned int contrast);
+
+/* Initialization */
+
+static inline void up_clear(FAR struct ug_dev_s *priv);
+
+/**************************************************************************************
+ * Private Data
+ **************************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane. This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8). Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently. However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[UG_XSTRIDE+1];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+ .fmt = UG_COLORFMT, /* Color format: RGB16-565: RRRR RGGG GGGB BBBB */
+ .xres = UG_XRES, /* Horizontal resolution in pixel columns */
+ .yres = UG_YRES, /* Vertical resolution in pixel rows */
+ .nplanes = 1, /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+ .putrun = ug_putrun, /* Put a run into LCD memory */
+ .getrun = ug_getrun, /* Get a run from LCD memory */
+ .buffer = (uint8_t*)g_runbuffer, /* Run scratch buffer */
+ .bpp = UG_BPP, /* Bits-per-pixel */
+};
+
+/* This is the standard, NuttX LCD driver object */
+
+static struct ug_dev_s g_ugdev =
+{
+ .dev =
+ {
+ /* LCD Configuration */
+
+ .getvideoinfo = ug_getvideoinfo,
+ .getplaneinfo = ug_getplaneinfo,
+
+ /* LCD RGB Mapping -- Not supported */
+ /* Cursor Controls -- Not supported */
+
+ /* LCD Specific Controls */
+
+ .getpower = ug_getpower,
+ .setpower = ug_setpower,
+ .getcontrast = ug_getcontrast,
+ .setcontrast = ug_setcontrast,
+ },
+};
+
+/**************************************************************************************
+ * Private Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: ug_powerstring
+ *
+ * Description:
+ * Convert the power setting to a string.
+ *
+ **************************************************************************************/
+
+
+static inline FAR const char *ug_powerstring(uint8_t power)
+{
+ if (power == UG_POWER_OFF)
+ {
+ return "OFF";
+ }
+ else if (power == UG_POWER_DIM)
+ {
+ return "DIM";
+ }
+ else if (power == UG_POWER_ON)
+ {
+ return "ON";
+ }
+ else
+ {
+ return "ERROR";
+ }
+}
+
+/**************************************************************************************
+ * Function: ug_select
+ *
+ * Description:
+ * Select the SPI, locking and re-configuring if necessary
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ **************************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void ug_select(FAR struct spi_dev_s *spi)
+{
+ /* We own the SPI bus, so just select the chip */
+
+ SPI_SELECT(spi, SPIDEV_DISPLAY, true);
+}
+#else
+static void ug_select(FAR struct spi_dev_s *spi)
+{
+ /* Select UG-9664HSWAG01 chip (locking the SPI bus in case there are multiple
+ * devices competing for the SPI bus
+ */
+
+ SPI_LOCK(spi, true);
+ SPI_SELECT(spi, SPIDEV_DISPLAY, true);
+
+ /* Now make sure that the SPI bus is configured for the UG-9664HSWAG01 (it
+ * might have gotten configured for a different device while unlocked)
+ */
+
+ SPI_SETMODE(spi, CONFIG_UG9664HSWAG01_SPIMODE);
+ SPI_SETBITS(spi, 8);
+#ifdef CONFIG_UG9664HSWAG01_FREQUENCY
+ SPI_SETFREQUENCY(spi, CONFIG_UG9664HSWAG01_FREQUENCY);
+#endif
+}
+#endif
+
+/**************************************************************************************
+ * Function: ug_deselect
+ *
+ * Description:
+ * De-select the SPI
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ **************************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void ug_deselect(FAR struct spi_dev_s *spi)
+{
+ /* We own the SPI bus, so just de-select the chip */
+
+ SPI_SELECT(spi, SPIDEV_DISPLAY, false);
+}
+#else
+static void ug_deselect(FAR struct spi_dev_s *spi)
+{
+ /* De-select UG-9664HSWAG01 chip and relinquish the SPI bus. */
+
+ SPI_SELECT(spi, SPIDEV_DISPLAY, false);
+ SPI_LOCK(spi, false);
+}
+#endif
+
+/**************************************************************************************
+ * Name: ug_putrun
+ *
+ * Description:
+ * This method can be used to write a partial raster line to the LCD:
+ *
+ * row - Starting row to write to (range: 0 <= row < yres)
+ * col - Starting column to write to (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer containing the run to be written to the LCD
+ * npixels - The number of pixels to write to the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+static int ug_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
+ size_t npixels)
+{
+ /* Because of this line of code, we will only be able to support a single UG device */
+
+ FAR struct ug_dev_s *priv = &g_ugdev;
+ FAR uint8_t *fbptr;
+ FAR uint8_t *ptr;
+ uint8_t devcol;
+ uint8_t fbmask;
+ uint8_t page;
+ uint8_t usrmask;
+ uint8_t i;
+ int pixlen;
+
+ gvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer);
+
+ /* Clip the run to the display */
+
+ pixlen = npixels;
+ if ((unsigned int)col + (unsigned int)pixlen > (unsigned int)UG_XRES)
+ {
+ pixlen = (int)UG_XRES - (int)col;
+ }
+
+ /* Verify that some portion of the run remains on the display */
+
+ if (pixlen <= 0 || row > UG_YRES)
+ {
+ return OK;
+ }
+
+ /* Get the page number. The range of 64 lines is divided up into eight
+ * pages of 8 lines each.
+ */
+
+ page = row >> 3;
+
+ /* Update the shadow frame buffer memory. First determine the pixel
+ * position in the frame buffer memory. Pixels are organized like
+ * this:
+ *
+ * --------+---+---+---+---+-...-+-----+
+ * Segment | 0 | 1 | 2 | 3 | ... | 131 |
+ * --------+---+---+---+---+-...-+-----+
+ * Bit 0 | | X | | | | |
+ * Bit 1 | | X | | | | |
+ * Bit 2 | | X | | | | |
+ * Bit 3 | | X | | | | |
+ * Bit 4 | | X | | | | |
+ * Bit 5 | | X | | | | |
+ * Bit 6 | | X | | | | |
+ * Bit 7 | | X | | | | |
+ * --------+---+---+---+---+-...-+-----+
+ *
+ * So, in order to draw a white, horizontal line, at row 45. we
+ * would have to modify all of the bytes in page 45/8 = 5. We
+ * would have to set bit 45%8 = 5 in every byte in the page.
+ */
+
+ fbmask = 1 << (row & 7);
+ fbptr = &priv->fb[page * UG_XRES + col];
+ ptr = fbptr;
+#ifdef CONFIG_NX_PACKEDMSFIRST
+ usrmask = MS_BIT;
+#else
+ usrmask = LS_BIT;
+#endif
+
+ for (i = 0; i < pixlen; i++)
+ {
+ /* Set or clear the corresponding bit */
+
+ if ((*buffer & usrmask) != 0)
+ {
+ *ptr++ |= fbmask;
+ }
+ else
+ {
+ *ptr++ &= ~fbmask;
+ }
+
+ /* Inc/Decrement to the next source pixel */
+
+#ifdef CONFIG_NX_PACKEDMSFIRST
+ if (usrmask == LS_BIT)
+ {
+ buffer++;
+ usrmask = MS_BIT;
+ }
+ else
+ {
+ usrmask >>= 1;
+ }
+#else
+ if (usrmask == MS_BIT)
+ {
+ buffer++;
+ usrmask = LS_BIT;
+ }
+ else
+ {
+ usrmask <<= 1;
+ }
+#endif
+ }
+
+ /* Offset the column position to account for smaller horizontal
+ * display range.
+ */
+
+ devcol = col + UG_XOFFSET;
+
+ /* Select and lock the device */
+
+ ug_select(priv->spi);
+
+ /* Select command transfer */
+
+ SPI_CMDDATA(priv->spi, SPIDEV_DISPLAY, true);
+
+ /* Set the starting position for the run */
+
+ (void)SPI_SEND(priv->spi, SSD1305_SETPAGESTART+page); /* Set the page start */
+ (void)SPI_SEND(priv->spi, SSD1305_SETCOLL + (devcol & 0x0f)); /* Set the low column */
+ (void)SPI_SEND(priv->spi, SSD1305_SETCOLH + (devcol >> 4)); /* Set the high column */
+
+ /* Select data transfer */
+
+ SPI_CMDDATA(priv->spi, SPIDEV_DISPLAY, false);
+
+ /* Then transfer all of the data */
+
+ (void)SPI_SNDBLOCK(priv->spi, fbptr, pixlen);
+
+ /* Unlock and de-select the device */
+
+ ug_deselect(priv->spi);
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: ug_getrun
+ *
+ * Description:
+ * This method can be used to read a partial raster line from the LCD:
+ *
+ * row - Starting row to read from (range: 0 <= row < yres)
+ * col - Starting column to read read (range: 0 <= col <= xres-npixels)
+ * buffer - The buffer in which to return the run read from the LCD
+ * npixels - The number of pixels to read from the LCD
+ * (range: 0 < npixels <= xres-col)
+ *
+ **************************************************************************************/
+
+static int ug_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
+ size_t npixels)
+{
+ /* Because of this line of code, we will only be able to support a single UG device */
+
+ FAR struct ug_dev_s *priv = &g_ugdev;
+ FAR uint8_t *fbptr;
+ uint8_t page;
+ uint8_t fbmask;
+ uint8_t usrmask;
+ uint8_t i;
+ int pixlen;
+
+ gvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
+ DEBUGASSERT(buffer);
+
+ /* Clip the run to the display */
+
+ pixlen = npixels;
+ if ((unsigned int)col + (unsigned int)pixlen > (unsigned int)UG_XRES)
+ {
+ pixlen = (int)UG_XRES - (int)col;
+ }
+
+ /* Verify that some portion of the run is actually the display */
+
+ if (pixlen <= 0 || row > UG_YRES)
+ {
+ return -EINVAL;
+ }
+
+ /* Then transfer the display data from the shadow frame buffer memory */
+ /* Get the page number. The range of 64 lines is divided up into eight
+ * pages of 8 lines each.
+ */
+
+ page = row >> 3;
+
+ /* Update the shadow frame buffer memory. First determine the pixel
+ * position in the frame buffer memory. Pixels are organized like
+ * this:
+ *
+ * --------+---+---+---+---+-...-+-----+
+ * Segment | 0 | 1 | 2 | 3 | ... | 131 |
+ * --------+---+---+---+---+-...-+-----+
+ * Bit 0 | | X | | | | |
+ * Bit 1 | | X | | | | |
+ * Bit 2 | | X | | | | |
+ * Bit 3 | | X | | | | |
+ * Bit 4 | | X | | | | |
+ * Bit 5 | | X | | | | |
+ * Bit 6 | | X | | | | |
+ * Bit 7 | | X | | | | |
+ * --------+---+---+---+---+-...-+-----+
+ *
+ * So, in order to draw a white, horizontal line, at row 45. we
+ * would have to modify all of the bytes in page 45/8 = 5. We
+ * would have to set bit 45%8 = 5 in every byte in the page.
+ */
+
+ fbmask = 1 << (row & 7);
+ fbptr = &priv->fb[page * UG_XRES + col];
+#ifdef CONFIG_NX_PACKEDMSFIRST
+ usrmask = MS_BIT;
+#else
+ usrmask = LS_BIT;
+#endif
+
+ *buffer = 0;
+ for (i = 0; i < pixlen; i++)
+ {
+ /* Set or clear the corresponding bit */
+
+ uint8_t byte = *fbptr++;
+ if ((byte & fbmask) != 0)
+ {
+ *buffer |= usrmask;
+ }
+
+ /* Inc/Decrement to the next destination pixel. Hmmmm. It looks like
+ * this logic could write past the end of the user buffer. Revisit
+ * this!
+ */
+
+#ifdef CONFIG_NX_PACKEDMSFIRST
+ if (usrmask == LS_BIT)
+ {
+ buffer++;
+ *buffer = 0;
+ usrmask = MS_BIT;
+ }
+ else
+ {
+ usrmask >>= 1;
+ }
+#else
+ if (usrmask == MS_BIT)
+ {
+ buffer++;
+ *buffer = 0;
+ usrmask = LS_BIT;
+ }
+ else
+ {
+ usrmask <<= 1;
+ }
+#endif
+ }
+
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: ug_getvideoinfo
+ *
+ * Description:
+ * Get information about the LCD video controller configuration.
+ *
+ **************************************************************************************/
+
+static int ug_getvideoinfo(FAR struct lcd_dev_s *dev,
+ FAR struct fb_videoinfo_s *vinfo)
+{
+ DEBUGASSERT(dev && vinfo);
+ gvdbg("fmt: %d xres: %d yres: %d nplanes: %d\n",
+ g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres, g_videoinfo.nplanes);
+ memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: ug_getplaneinfo
+ *
+ * Description:
+ * Get information about the configuration of each LCD color plane.
+ *
+ **************************************************************************************/
+
+static int ug_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+ FAR struct lcd_planeinfo_s *pinfo)
+{
+ DEBUGASSERT(dev && pinfo && planeno == 0);
+ gvdbg("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+ memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: ug_getpower
+ *
+ * Description:
+ * Get the LCD panel power status (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int ug_getpower(struct lcd_dev_s *dev)
+{
+ struct ug_dev_s *priv = (struct ug_dev_s *)dev;
+ DEBUGASSERT(priv);
+ gvdbg("powered: %s\n", ug_powerstring(priv->powered));
+ return priv->powered;
+}
+
+/**************************************************************************************
+ * Name: ug_setpower
+ *
+ * Description:
+ * Enable/disable LCD panel power (0: full off - CONFIG_LCD_MAXPOWER: full on). On
+ * backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ **************************************************************************************/
+
+static int ug_setpower(struct lcd_dev_s *dev, int power)
+{
+ struct ug_dev_s *priv = (struct ug_dev_s *)dev;
+
+ DEBUGASSERT(priv && (unsigned)power <= CONFIG_LCD_MAXPOWER);
+ gvdbg("power: %s powered: %s\n",
+ ug_powerstring(power), ug_powerstring(priv->powered));
+
+ /* Select and lock the device */
+
+ ug_select(priv->spi);
+ if (power <= UG_POWER_OFF)
+ {
+ /* Turn the display off */
+
+ (void)SPI_SEND(priv->spi, SSD1305_DISPOFF); /* Display off */
+
+ /* Remove power to the device */
+
+ ug_power(0, false);
+ priv->powered = UG_POWER_OFF;
+ }
+ else
+ {
+ /* Turn the display on, dim or normal */
+
+ if (power == UG_POWER_DIM)
+ {
+ (void)SPI_SEND(priv->spi, SSD1305_DISPONDIM); /* Display on, dim mode */
+ }
+ else /* if (power > UG_POWER_DIM) */
+ {
+ (void)SPI_SEND(priv->spi, SSD1305_DISPON); /* Display on, normal mode */
+ power = UG_POWER_ON;
+ }
+ (void)SPI_SEND(priv->spi, SSD1305_DISPRAM); /* Resume to RAM content display */
+
+ /* Restore power to the device */
+
+ ug_power(0, true);
+ priv->powered = power;
+ }
+ ug_deselect(priv->spi);
+
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: ug_getcontrast
+ *
+ * Description:
+ * Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int ug_getcontrast(struct lcd_dev_s *dev)
+{
+ struct ug_dev_s *priv = (struct ug_dev_s *)dev;
+ DEBUGASSERT(priv);
+ return (int)priv->contrast;
+}
+
+/**************************************************************************************
+ * Name: ug_setcontrast
+ *
+ * Description:
+ * Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ **************************************************************************************/
+
+static int ug_setcontrast(struct lcd_dev_s *dev, unsigned int contrast)
+{
+ struct ug_dev_s *priv = (struct ug_dev_s *)dev;
+
+ gvdbg("contrast: %d\n", contrast);
+ DEBUGASSERT(priv);
+
+ if (contrast > 255)
+ {
+ return -EINVAL;
+ }
+
+ /* Select and lock the device */
+
+ ug_select(priv->spi);
+
+ /* Select command transfer */
+
+ SPI_CMDDATA(priv->spi, SPIDEV_DISPLAY, true);
+
+ /* Set the contrast */
+
+ (void)SPI_SEND(priv->spi, SSD1305_SETCONTRAST); /* Set contrast control register */
+ (void)SPI_SEND(priv->spi, contrast); /* Data 1: Set 1 of 256 contrast steps */
+ priv->contrast = contrast;
+
+ /* Unlock and de-select the device */
+
+ ug_deselect(priv->spi);
+ return OK;
+}
+
+/**************************************************************************************
+ * Name: up_clear
+ *
+ * Description:
+ * Clear the display.
+ *
+ **************************************************************************************/
+
+static inline void up_clear(FAR struct ug_dev_s *priv)
+{
+ FAR struct spi_dev_s *spi = priv->spi;
+ int page;
+ int i;
+
+ /* Clear the framebuffer */
+
+ memset(priv->fb, UG_Y1_BLACK, UG_FBSIZE);
+
+ /* Select and lock the device */
+
+ ug_select(priv->spi);
+
+ /* Go through all 8 pages */
+
+ for (page = 0, i = 0; i < 8; i++)
+ {
+ /* Select command transfer */
+
+ SPI_CMDDATA(spi, SPIDEV_DISPLAY, true);
+
+ /* Set the starting position for the run */
+
+ (void)SPI_SEND(priv->spi, SSD1305_SETPAGESTART+i);
+ (void)SPI_SEND(priv->spi, SSD1305_SETCOLL + (UG_XOFFSET & 0x0f));
+ (void)SPI_SEND(priv->spi, SSD1305_SETCOLH + (UG_XOFFSET >> 4));
+
+ /* Select data transfer */
+
+ SPI_CMDDATA(spi, SPIDEV_DISPLAY, false);
+
+ /* Then transfer all 96 columns of data */
+
+ (void)SPI_SNDBLOCK(priv->spi, &priv->fb[page * UG_XRES], UG_XRES);
+ }
+
+ /* Unlock and de-select the device */
+
+ ug_deselect(spi);
+}
+
+/**************************************************************************************
+ * Public Functions
+ **************************************************************************************/
+
+/**************************************************************************************
+ * Name: ug_initialize
+ *
+ * Description:
+ * Initialize the UG-9664HSWAG01 video hardware. The initial state of the
+ * OLED is fully initialized, display memory cleared, and the OLED ready to
+ * use, but with the power setting at 0 (full off == sleep mode).
+ *
+ * Input Parameters:
+ *
+ * spi - A reference to the SPI driver instance.
+ * devno - A value in the range of 0 through CONFIG_UG9664HSWAG01_NINTERFACES-1.
+ * This allows support for multiple OLED devices.
+ *
+ * Returned Value:
+ *
+ * On success, this function returns a reference to the LCD object for the specified
+ * OLED. NULL is returned on any failure.
+ *
+ **************************************************************************************/
+
+FAR struct lcd_dev_s *ug_initialize(FAR struct spi_dev_s *spi, unsigned int devno)
+{
+ /* Configure and enable LCD */
+
+ FAR struct ug_dev_s *priv = &g_ugdev;
+
+ gvdbg("Initializing\n");
+ DEBUGASSERT(spi && devno == 0);
+
+ /* Save the reference to the SPI device */
+
+ priv->spi = spi;
+
+ /* Select and lock the device */
+
+ ug_select(spi);
+
+ /* Make sure that the OLED off */
+
+ ug_power(0, false);
+
+ /* Select command transfer */
+
+ SPI_CMDDATA(spi, SPIDEV_DISPLAY, true);
+
+ /* Set the starting position for the run */
+
+ (void)SPI_SEND(spi, SSD1305_SETCOLL + 2); /* Set low column address */
+ (void)SPI_SEND(spi, SSD1305_SETCOLH + 2); /* Set high column address */
+ (void)SPI_SEND(spi, SSD1305_SETSTARTLINE+0); /* Display start set */
+ (void)SPI_SEND(spi, SSD1305_SCROLL_STOP); /* Stop horizontal scroll */
+ (void)SPI_SEND(spi, SSD1305_SETCONTRAST); /* Set contrast control register */
+ (void)SPI_SEND(spi, 0x32); /* Data 1: Set 1 of 256 contrast steps */
+ (void)SPI_SEND(spi, SSD1305_SETBRIGHTNESS); /* Brightness for color bank */
+ (void)SPI_SEND(spi, 0x80); /* Data 1: Set 1 of 256 contrast steps */
+ (void)SPI_SEND(spi, SSD1305_MAPCOL131); /* Set segment re-map */
+ (void)SPI_SEND(spi, SSD1305_DISPNORMAL); /* Set normal display */
+/*(void)SPI_SEND(spi, SSD1305_DISPINVERTED); Set inverse display */
+ (void)SPI_SEND(spi, SSD1305_SETMUX); /* Set multiplex ratio */
+ (void)SPI_SEND(spi, 0x3f); /* Data 1: MUX ratio -1: 15-63 */
+ (void)SPI_SEND(spi, SSD1305_SETOFFSET); /* Set display offset */
+ (void)SPI_SEND(spi, 0x40); /* Data 1: Vertical shift by COM: 0-63 */
+ (void)SPI_SEND(spi, SSD1305_MSTRCONFIG); /* Set dc-dc on/off */
+ (void)SPI_SEND(spi, SSD1305_MSTRCONFIG_EXTVCC); /* Data 1: Select external Vcc */
+ (void)SPI_SEND(spi, SSD1305_SETCOMREMAPPED); /* Set com output scan direction */
+ (void)SPI_SEND(spi, SSD1305_SETDCLK); /* Set display clock divide
+ * ratio/oscillator/frequency */
+ (void)SPI_SEND(spi, 15 << SSD1305_DCLKFREQ_SHIFT | 0 << SSD1305_DCLKDIV_SHIFT);
+ (void)SPI_SEND(spi, SSD1305_SETCOLORMODE); /* Set area color mode on/off & low power
+ * display mode */
+ (void)SPI_SEND(spi, SSD1305_COLORMODE_MONO | SSD1305_POWERMODE_LOW);
+ (void)SPI_SEND(spi, SSD1305_SETPRECHARGE); /* Set pre-charge period */
+ (void)SPI_SEND(spi, 15 << SSD1305_PHASE2_SHIFT | 1 << SSD1305_PHASE1_SHIFT);
+ (void)SPI_SEND(spi, SSD1305_SETCOMCONFIG); /* Set COM configuration */
+ (void)SPI_SEND(spi, SSD1305_COMCONFIG_ALT); /* Data 1, Bit 4: 1=Alternative COM pin configuration */
+ (void)SPI_SEND(spi, SSD1305_SETVCOMHDESEL); /* Set VCOMH deselect level */
+ (void)SPI_SEND(spi, SSD1305_VCOMH_x7p7); /* Data 1: ~0.77 x Vcc */
+ (void)SPI_SEND(spi, SSD1305_SETLUT); /* Set look up table for area color */
+ (void)SPI_SEND(spi, 0x3f); /* Data 1: Pulse width: 31-63 */
+ (void)SPI_SEND(spi, 0x3f); /* Data 2: Color A: 31-63 */
+ (void)SPI_SEND(spi, 0x3f); /* Data 3: Color B: 31-63 */
+ (void)SPI_SEND(spi, 0x3f); /* Data 4: Color C: 31-63 */
+ (void)SPI_SEND(spi, SSD1305_DISPON); /* Display on, normal mode */
+ (void)SPI_SEND(spi, SSD1305_DISPRAM); /* Resume to RAM content display */
+
+ /* Let go of the SPI lock and de-select the device */
+
+ ug_deselect(spi);
+
+ /* Clear the framebuffer */
+
+ up_mdelay(100);
+ up_clear(priv);
+ return &priv->dev;
+}
diff --git a/nuttx/drivers/loop.c b/nuttx/drivers/loop.c
new file mode 100644
index 000000000..df96da59f
--- /dev/null
+++ b/nuttx/drivers/loop.c
@@ -0,0 +1,509 @@
+/****************************************************************************
+ * drivers/loop.c
+ *
+ * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define loop_semgive(d) sem_post(&(d)->sem) /* To match loop_semtake */
+#define MAX_OPENCNT (255) /* Limit of uint8_t */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct loop_struct_s
+{
+ sem_t sem; /* For safe read-modify-write operations */
+ uint32_t nsectors; /* Number of sectors on device */
+ off_t offset; /* Offset (in bytes) to the first sector */
+ uint16_t sectsize; /* The size of one sector */
+ uint8_t opencnt; /* Count of open references to the loop device */
+#ifdef CONFIG_FS_WRITABLE
+ bool writeenabled; /* true: can write to device */
+#endif
+ int fd; /* Descriptor of char device/file */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void loop_semtake(FAR struct loop_struct_s *dev);
+static int loop_open(FAR struct inode *inode);
+static int loop_close(FAR struct inode *inode);
+static ssize_t loop_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors);
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t loop_write(FAR struct inode *inode, const unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors);
+#endif
+static int loop_geometry(FAR struct inode *inode, struct geometry *geometry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct block_operations g_bops =
+{
+ loop_open, /* open */
+ loop_close, /* close */
+ loop_read, /* read */
+#ifdef CONFIG_FS_WRITABLE
+ loop_write, /* write */
+#else
+ NULL, /* write */
+#endif
+ loop_geometry, /* geometry */
+ NULL /* ioctl */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: loop_semtake
+ ****************************************************************************/
+
+static void loop_semtake(FAR struct loop_struct_s *dev)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&dev->sem) != 0)
+ {
+ /* The only case that an error should occur here is if
+ * the wait was awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: loop_open
+ *
+ * Description: Open the block device
+ *
+ ****************************************************************************/
+
+static int loop_open(FAR struct inode *inode)
+{
+ FAR struct loop_struct_s *dev;
+ int ret = OK;
+
+ DEBUGASSERT(inode && inode->i_private);
+ dev = (FAR struct loop_struct_s *)inode->i_private;
+
+ /* Make sure we have exclusive access to the state structure */
+
+ loop_semtake(dev);
+ if (dev->opencnt == MAX_OPENCNT)
+ {
+ return -EMFILE;
+ }
+ else
+ {
+ /* Increment the open count */
+
+ dev->opencnt++;
+ }
+
+ loop_semgive(dev);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: loop_close
+ *
+ * Description: close the block device
+ *
+ ****************************************************************************/
+
+static int loop_close(FAR struct inode *inode)
+{
+ FAR struct loop_struct_s *dev;
+ int ret = OK;
+
+ DEBUGASSERT(inode && inode->i_private);
+ dev = (FAR struct loop_struct_s *)inode->i_private;
+
+ /* Make sure we have exclusive access to the state structure */
+
+ loop_semtake(dev);
+ if (dev->opencnt == 0)
+ {
+ return -EIO;
+ }
+ else
+ {
+ /* Decrement the open count */
+
+ dev->opencnt--;
+ }
+
+ loop_semgive(dev);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: loop_read
+ *
+ * Description: Read the specified numer of sectors
+ *
+ ****************************************************************************/
+
+static ssize_t loop_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
+{
+ FAR struct loop_struct_s *dev;
+ size_t nbytesread;
+ off_t offset;
+ int ret;
+
+ DEBUGASSERT(inode && inode->i_private);
+ dev = (FAR struct loop_struct_s *)inode->i_private;
+
+ if (start_sector + nsectors > dev->nsectors)
+ {
+ dbg("Read past end of file\n");
+ return -EIO;
+ }
+
+ /* Calculate the offset to read the sectors and seek to the position */
+
+ offset = start_sector * dev->sectsize + dev->offset;
+ ret = lseek(dev->fd, offset, SEEK_SET);
+ if (ret == (off_t)-1)
+ {
+ dbg("Seek failed for offset=%d: %d\n", (int)offset, errno);
+ return -EIO;
+ }
+
+ /* Then read the requested number of sectors from that position */
+
+ do
+ {
+ nbytesread = read(dev->fd, buffer, nsectors * dev->sectsize);
+ if (nbytesread < 0 && errno != EINTR)
+ {
+ dbg("Read failed: %d\n", errno);
+ return -errno;
+ }
+ }
+ while (nbytesread < 0);
+
+ /* Return the number of sectors read */
+
+ return nbytesread / dev->sectsize;
+}
+
+/****************************************************************************
+ * Name: loop_write
+ *
+ * Description: Write the specified number of sectors
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t loop_write(FAR struct inode *inode, const unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
+{
+ FAR struct loop_struct_s *dev;
+ size_t nbyteswritten;
+ off_t offset;
+ int ret;
+
+ DEBUGASSERT(inode && inode->i_private);
+ dev = (FAR struct loop_struct_s *)inode->i_private;
+
+ /* Calculate the offset to write the sectors and seek to the position */
+
+ offset = start_sector * dev->sectsize + dev->offset;
+ ret = lseek(dev->fd, offset, SEEK_SET);
+ if (ret == (off_t)-1)
+ {
+ dbg("Seek failed for offset=%d: %d\n", (int)offset, errno);
+ }
+
+ /* Then write the requested number of sectors to that position */
+
+ do
+ {
+ nbyteswritten = write(dev->fd, buffer, nsectors * dev->sectsize);
+ if (nbyteswritten < 0 && errno != EINTR)
+ {
+ dbg("Write failed: %d\n", errno);
+ return -errno;
+ }
+ }
+ while (nbyteswritten < 0);
+
+ /* Return the number of sectors written */
+
+ return nbyteswritten / dev->sectsize;
+}
+#endif
+
+/****************************************************************************
+ * Name: loop_geometry
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int loop_geometry(FAR struct inode *inode, struct geometry *geometry)
+{
+ FAR struct loop_struct_s *dev;
+
+ DEBUGASSERT(inode);
+ if (geometry)
+ {
+ dev = (FAR struct loop_struct_s *)inode->i_private;
+ geometry->geo_available = true;
+ geometry->geo_mediachanged = false;
+#ifdef CONFIG_FS_WRITABLE
+ geometry->geo_writeenabled = dev->writeenabled;
+#else
+ geometry->geo_writeenabled = false;
+#endif
+ geometry->geo_nsectors = dev->nsectors;
+ geometry->geo_sectorsize = dev->sectsize;
+ return OK;
+ }
+
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: losetup
+ *
+ * Description:
+ * Setup the loop device so that it exports the file referenced by 'filename'
+ * as a block device.
+ *
+ ****************************************************************************/
+
+int losetup(const char *devname, const char *filename, uint16_t sectsize,
+ off_t offset, bool readonly)
+{
+ FAR struct loop_struct_s *dev;
+ struct stat sb;
+ int ret;
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (!devname || !filename || !sectsize)
+ {
+ return -EINVAL;
+ }
+#endif
+
+ /* Get the size of the file */
+
+ ret = stat(filename, &sb);
+ if (ret < 0)
+ {
+ dbg("Failed to stat %s: %d\n", filename, errno);
+ return -errno;
+ }
+
+ /* Check if the file system is big enough for one block */
+
+ if (sb.st_size - offset < sectsize)
+ {
+ dbg("File is too small for blocksize\n");
+ return -ERANGE;
+ }
+
+ /* Allocate a loop device structure */
+
+ dev = (FAR struct loop_struct_s *)kzalloc(sizeof(struct loop_struct_s));
+ if (!dev)
+ {
+ return -ENOMEM;
+ }
+
+ /* Initialize the loop device structure. */
+
+ sem_init(&dev->sem, 0, 1);
+ dev->nsectors = (sb.st_size - offset) / sectsize;
+ dev->sectsize = sectsize;
+ dev->offset = offset;
+
+ /* Open the file. */
+
+#ifdef CONFIG_FS_WRITABLE
+ dev->writeenabled = false; /* Assume failure */
+ dev->fd = -1;
+
+ /* First try to open the device R/W access (unless we are asked
+ * to open it readonly).
+ */
+
+ if (!readonly)
+ {
+ dev->fd = open(filename, O_RDWR);
+ }
+
+ if (dev->fd >= 0)
+ {
+ dev->writeenabled = true; /* Success */
+ }
+ else
+#endif
+ {
+ /* If that fails, then try to open the device read-only */
+
+ dev->fd = open(filename, O_RDWR);
+ if (dev->fd < 0)
+ {
+ dbg("Failed to open %s: %d\n", filename, errno);
+ ret = -errno;
+ goto errout_with_dev;
+ }
+ }
+
+ /* Inode private data will be reference to the loop device structure */
+
+ ret = register_blockdriver(devname, &g_bops, 0, dev);
+ if (ret < 0)
+ {
+ fdbg("register_blockdriver failed: %d\n", -ret);
+ goto errout_with_fd;
+ }
+
+ return OK;
+
+errout_with_fd:
+ close(dev->fd);
+errout_with_dev:
+ kfree(dev);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: loteardown
+ *
+ * Description:
+ * Undo the setup performed by losetup
+ *
+ ****************************************************************************/
+
+int loteardown(const char *devname)
+{
+ FAR struct loop_struct_s *dev;
+ FAR struct inode *inode;
+ int ret;
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (!devname)
+ {
+ return -EINVAL;
+ }
+#endif
+
+ /* Open the block driver associated with devname so that we can get the inode
+ * reference.
+ */
+
+ ret = open_blockdriver(devname, MS_RDONLY, &inode);
+ if (ret < 0)
+ {
+ dbg("Failed to open %s: %d\n", devname, -ret);
+ return ret;
+ }
+
+ /* Inode private data is a reference to the loop device stgructure */
+
+ dev = (FAR struct loop_struct_s *)inode->i_private;
+ close_blockdriver(inode);
+
+ DEBUGASSERT(dev);
+
+ /* Are there still open references to the device */
+
+ if (dev->opencnt > 0)
+ {
+ return -EBUSY;
+ }
+
+ /* Otherwise, unregister the block device */
+
+ ret = unregister_blockdriver(devname);
+
+ /* Release the device structure */
+
+ if (dev->fd >= 0)
+ {
+ (void)close(dev->fd);
+ }
+
+ kfree(dev);
+ return ret;
+}
diff --git a/nuttx/drivers/mmcsd/Kconfig b/nuttx/drivers/mmcsd/Kconfig
new file mode 100644
index 000000000..c224f220a
--- /dev/null
+++ b/nuttx/drivers/mmcsd/Kconfig
@@ -0,0 +1,77 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+config MMCSD_NSLOTS
+ int "Number of MMC/SD slots"
+ default 1
+ ---help---
+ Number of MMC/SD slots supported by the
+ driver. Default is one.
+
+config MMCSD_READONLY
+ bool "Disable MMC/SD write access"
+ default n
+ ---help---
+ Provide read-only access. Default is
+ Read/Write
+
+config MMCSD_MULTIBLOCK_DISABLE
+ bool "Disable MMC/SD multiblock transfer"
+ default n
+ ---help---
+ Use only the single block transfer method.
+ This setting is used to work around buggy SDIO drivers that cannot handle
+ multiple block transfers.
+
+config MMCSD_MMCSUPPORT
+ bool "MMC cards support"
+ default y
+ ---help---
+ Enable support for MMC cards
+
+config MMCSD_HAVECARDDETECT
+ bool "MMC/SD card detection"
+ default y
+ ---help---
+ SDIO driver card detection is
+ 100% accurate
+
+config MMCSD_SPI
+ bool "MMC/SD spi transfer support"
+ default y
+
+config MMCSD_SPICLOCK
+ int "MMC/SD maximum SPI clock"
+ default 20000000
+ depends on MMCSD_SPI
+ ---help---
+ Maximum SPI clock to drive MMC/SD card.
+ Default is 20MHz.
+
+config MMCSD_SDIO
+ bool "MMC/SD sdio transfer support"
+ default y
+
+if MMCSD_SDIO
+config SDIO_DMA
+ bool "SDIO dma support"
+ default n
+ ---help---
+ SDIO driver supports DMA
+
+config SDIO_MUXBUS
+ bool "SDIO bus share support"
+ default n
+ ---help---
+ Set this SDIO interface if the SDIO interface
+ or hardware resources are shared with other drivers.
+
+config SDIO_WIDTH_D1_ONLY
+ bool "SDIO 1-bit transfer"
+ default n
+ ---help---
+ Select 1-bit transfer mode. Default:
+ 4-bit transfer mode.
+endif
+
diff --git a/nuttx/drivers/mmcsd/Make.defs b/nuttx/drivers/mmcsd/Make.defs
new file mode 100644
index 000000000..850456597
--- /dev/null
+++ b/nuttx/drivers/mmcsd/Make.defs
@@ -0,0 +1,46 @@
+############################################################################
+# drivers/mmcsd/Make.defs
+#
+# Copyright (C) 2008, 2011 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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 MMC/SD drivers
+
+CSRCS += mmcsd_sdio.c mmcsd_spi.c mmcsd_debug.c
+
+# Include MMC/SD driver build support
+
+DEPPATH += --dep-path mmcsd
+VPATH += :mmcsd
+CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/mmcsd}
+
+
diff --git a/nuttx/drivers/mmcsd/mmcsd_csd.h b/nuttx/drivers/mmcsd/mmcsd_csd.h
new file mode 100644
index 000000000..d5343aa84
--- /dev/null
+++ b/nuttx/drivers/mmcsd/mmcsd_csd.h
@@ -0,0 +1,424 @@
+/****************************************************************************
+ * drivers/mmcsd/mmcsd_csd.h
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_MMCSD_MMCSD_CSD_H
+#define __DRIVERS_MMCSD_MMCSD_CSD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+/* CSD **********************************************************************/
+
+/* The CSD is a 16 byte, / 128-bit packed structure. The following macros
+ * can be used to extract each packed field from the CSD. Two types of
+ * macros are supported, selected by CONFIG_MMCSD_BE16: (1) byte data (with
+ * MS byte first), or (2) 16-bit big-endian data (with MS hword first).
+ */
+
+#ifdef CONFIG_MMCSD_BE16
+
+/* CSD_STRUCTURE 126-127 */
+
+#define MMCSD_CSD_CSDSTRUCT(csd) (csd[0] >> 14)
+
+/* SPEC_VERS 122-125 = (word 0, bits 13:10) (MMC) Spec version */
+
+#define MMC_CSD_SPECVERS(csd) ((csd[0] >> 10) & 0x0f)
+
+/* Reserved 120-125 */
+
+/* TAAC 112-119 = Data read access-time-1
+ * TIME_VALUE 3-6 = Time mantissa
+ * TIME_UNIT 0-2 = Time exponent
+ */
+
+#define MMCSD_CSD_TAAC_TIMEVALUE(csd) ((csd[0] >> 3) & 0x0f)
+#define MMCSD_CSD_TAAC_TIMEUNIT(csd) (csd[0] & 7)
+
+/* NSAC 111:104 = Data read access-time-2 in CLK cycle(NSAC*100) */
+
+#define MMCSD_CSD_NSAC(csd) (csd[1] >> 8)
+
+/* TRAN_SPEED 96-103 = Max. data transfer rate
+ * TIME_VALUE 3-6 = Rate exponent
+ * TRANSFER_RATE_UNIT 0-2 = Rate mantissa
+ */
+
+#define MMCSD_CSD_TRANSPEED_TIMEVALUE(csd) ((csd[1] >> 3) & 0x0f)
+#define MMCSD_CSD_TRANSPEED_TRANSFERRATEUNIT(csd) (csd[1] & 7)
+
+/* CCC 84-95 = Card command classes */
+
+#define MMCSD_CSD_CCC(csd) ((csd[2] >> 4) & 0x0fff)
+
+ /* READ_BL_LEN 80-83 = Max. read data block length */
+
+#define MMCSD_CSD_READBLLEN(csd) (csd[2] & 0x0f)
+
+/* READ_BL_PARTIAL 79-79 = Partial blocks for read allowed */
+
+#define MMCSD_CSD_READBLPARTIAL(csd) (csd[3] >> 15)
+
+/* WRITE_BLK_MISALIGN 78-78 = Write block misalignment */
+
+#define MMCSD_CSD_WRITEBLKMISALIN(csd) ((csd[3] >> 14) & 1)
+
+/* READ_BLK_MISALIGN 77:77 = Read block misalignment */
+
+#define MMCSD_CSD_READBLKMISALIN(csd) ((csd[3] >> 13) & 1)
+
+/* DSR_IMP 76-76 = DSR implemented */
+
+#define MMCSD_CSD_DSRIMP(csd) ((csd[3] >> 12) & 1)
+
+/* C_SIZE 62-73 Device size */
+
+#define MMCSD_CSD_CSIZE(csd) (((csd[3] & 0x03ff) << 2) | ((csd[4] >> 14) & 3))
+
+/* VDD_R_CURR_MIN 59-61 = Max. read current at Vcc min */
+
+#define MMCSD_CSD_VDDRCURRMIN(csd) ((csd[4] >> 11) & 7)
+
+/* VDD_R_CURR_MAX 56-58 = Max. read current at Vcc max */
+
+#define MMCSD_CSD_VDDRCURRMAX(csd) ((csd[4] >> 8) & 7)
+
+/* VDD_W_CURR_MIN 53-55 = Max. write current at Vcc min */
+
+#define MMCSD_CSD_VDDWCURRMIN(csd) ((csd[4] >> 5) & 7)
+
+/* VDD_W_CURR_MAX 50-52 = Max. write current at Vcc max */
+
+#define MMCSD_CSD_VDDWCURRMAX(csd) ((csd[4] >> 2) & 7)
+
+/* C_SIZE_MULT 47-49 Device size multiplier */
+
+#define MMCSD_CSD_CSIZEMULT(csd) (((csd[4] & 3) << 1) | (csd[5] >> 15))
+
+/* ER_BLK_EN 46-46 =Erase single block enable (SD) */
+
+#define SD_CSD_SDERBLKEN(csd) ((csd[5] >> 14) & 1)
+
+/* SECTOR_SIZE 39-45 = Erase sector size (SD) */
+
+#define SD_CSD_SECTORSIZE(csd) ((csd[5] >> 7) & 0x7f)
+
+/* SECTOR_SIZE 42-46 = Erase sector size (MMC) */
+
+#define MMC_CSD_SECTORSIZE(csd) ((csd[5] >> 10) & 0x1f)
+
+/* ER_GRP_SIZE 37-41 = Erase group size (MMC)*/
+
+#define MMC_CSD_ERGRPSIZE(csd) ((csd[5] >> 5) & 0x1f)
+
+/* WP_GRP_SIZE 32-38 = Write protect group size (SD) */
+
+#define SD_CSD_WPGRPSIZE(csd) (csd[5] & 0x7f)
+
+/* WP_GRP_SIZE 32-36 = Write protect group size (MMC) */
+
+#define MMC_CSD_WPGRPSIZE(csd) (csd[5] & 0x1f)
+
+/* WP_GRP_EN 31-31 = Write protect group enable */
+
+#define MMCSD_WPGRPEN(csd) (csd[6] >> 15)
+
+/* DFLT_ECC 29-30 = Manufacturer default ECC (MMC) */
+
+#define MMC_CSD_DFLTECC(csd) ((csd[6] >> 13) & 3)
+
+/* R2W_FACTOR 26-28 = Write speed factor */
+
+#define MMCSD_CSD_R2WFACTOR(csd) ((csd[6] >> 10) & 7)
+
+/* WRITE_BL_LEN 22-25 = Max. write data block length */
+
+#define MMCSD_CSD_WRITEBLLEN(csd) ((csd[6] >> 6) & 0x0f)
+
+/* WRITE_BL_PARTIAL 21-21 = Partial blocks for write allowed */
+
+#define MMCSD_CSD_WRITEBLPARTIAL(csd) ((csd[6] >> 5) & 1)
+
+/* Reserved 16-20 */
+
+/* FILE_FORMAT_GROUP 15-15 = File format group */
+
+#define MMCSD_CSD_FILEFORMATGRP(csd) (csd[7] >> 15)
+
+/* COPY 14-14 = Copy flag (OTP) */
+
+#define MMCSD_CSD_COPY(csd) ((csd[7] >> 14) & 1)
+
+/* PERM_WRITE_PROTECT 13-13 = Permanent write protection */
+
+#define MMCSD_CSD_PERMWRITEPROTECT(csd) ((csd[7] >> 13) & 1)
+
+/* TMP_WRITE_PROTECT 12-12 = Temporary write protection */
+
+#define MMCSD_CSD_TMPWRITEPROTECT(csd) ((csd[7] >> 12) & 1)
+
+/* FILE_FORMAT 10-11 = File format */
+
+#define MMCSD_CSD_FILEFORMAT(csd) ((csd[7] >> 10) & 3)
+
+/* ECC 8-9 = ECC (MMC) */
+
+#define MMC_CSD_ECC(csd) ((csd[7] >> 8) & 3)
+
+/* CRC 1-7 = CRC */
+
+#define MMCSD_CSD_CRC(csd) ((csd[7] >> 1) & 0x7f)
+
+/* Reserved 0-0 */
+
+#else /* CONFIG_MMCSD_BE16 */
+
+/* CSD_STRUCTURE 126-127 */
+
+#define MMCSD_CSD_CSDSTRUCT(csd) (csd[0] >> 6)
+
+/* SPEC_VERS 122-125 = (word 0, bits 13:10) (MMC) Spec version */
+
+#define MMC_CSD_SPECVERS(csd) ((csd[0] >> 2) & 0x0f)
+
+/* Reserved 120-155 */
+
+/* TAAC 112-119 = Data read access-time-1
+ * TIME_VALUE 3-6 = Time mantissa
+ * TIME_UNIT 0-2 = Time exponent
+ */
+
+#define MMCSD_CSD_TAAC_TIMEVALUE(csd) ((csd[1] >> 3) & 0x0f)
+#define MMCSD_CSD_TAAC_TIMEUNIT(csd) (csd[1] & 7)
+
+#define SD20_CSD_TAC_TIMEVALUE(csd) (1)
+#define SD20_CSD_TAC_TIMEUNIT(csd) (6)
+
+/* NSAC 111:104 = Data read access-time-2 in CLK cycle(NSAC*100) */
+
+#define MMCSD_CSD_NSAC(csd) (csd[2])
+#define SD20_CSD_NSAC(csd) (0)
+
+/* TRAN_SPEED 96-103 = Max. data transfer rate
+ * TIME_VALUE 3-6 = Rate exponent
+ * TRANSFER_RATE_UNIT 0-2 = Rate mantissa
+ */
+
+#define MMCSD_CSD_TRANSPEED_TIMEVALUE(csd) ((csd[3] >> 3) & 0x0f)
+#define MMCSD_CSD_TRANSPEED_TRANSFERRATEUNIT(csd) (csd[3] & 7)
+
+#define SD20_CSD_TRANSPEED_TIMEVALUE(csd) MMCSD_CSD_TRANSPEED_TIMEVALUE(csd)
+#define SD20_CSD_TRANSPEED_TRANSFERRATEUNIT(csd) MMCSD_CSD_TRANSPEED_TRANSFERRATEUNIT(csd)
+
+/* CCC 84-95 = Card command classes */
+
+#define MMCSD_CSD_CCC(csd) (((uint16_t)csd[4] << 4) | ((uint16_t)csd[5] >> 4))
+#define SD20_CSD_CCC(csd) MMCSD_CSD_CCC(csd)
+
+ /* READ_BL_LEN 80-83 = Max. read data block length */
+
+#define MMCSD_CSD_READBLLEN(csd) (csd[5] & 0x0f)
+#define SD20_CSD_READBLLEN(csd) (9)
+
+/* READ_BL_PARTIAL 79-79 = Partial blocks for read allowed */
+
+#define MMCSD_CSD_READBLPARTIAL(csd) (csd[6] >> 7)
+#define SD20_CSD_READBLPARTIAL(csd) (0)
+
+/* WRITE_BLK_MISALIGN 78-78 = Write block misalignment */
+
+#define MMCSD_CSD_WRITEBLKMISALIGN(csd) ((csd[6] >> 6) & 1)
+#define SD20_CSD_WRITEBLKMISALIGN(csd) (0)
+
+/* READ_BLK_MISALIGN 77:77 = Read block misalignment */
+
+#define MMCSD_CSD_READBLKMISALIGN(csd) ((csd[6] >> 5) & 1)
+#define SD20_CSD_READBLKMISALIGN(csd) (0)
+
+/* DSR_IMP 76-76 = DSR implemented */
+
+#define MMCSD_CSD_DSRIMP(csd) ((csd[6] >> 4) & 1)
+#define SD20_CSD_DSRIMP(csd) MMCSD_CSD_DSRIMP(csd)
+
+/* C_SIZE 62-73 Device size */
+
+#define MMCSD_CSD_CSIZE(csd) (((csd[6] & 3) << 10) | (csd[7] << 2) | (csd[8] >> 6))
+#define SD20_CSD_CSIZE(csd) ((((uint32_t)csd[7] & 0x3f) << 16) | (csd[8] << 8) | csd[9])
+
+/* VDD_R_CURR_MIN 59-61 = Max. read current at Vcc min */
+
+#define MMCSD_CSD_VDDRCURRMIN(csd) ((csd[8] >> 3) & 7)
+#define SD20_CSD_VDDRCURRMIN(csd) (7)
+
+/* VDD_R_CURR_MAX 56-58 = Max. read current at Vcc max */
+
+#define MMCSD_CSD_VDDRCURRMAX(csd) (csd[8] & 7)
+#define SD20_CSD_VDDRCURRMAX(csd) (6)
+
+/* VDD_W_CURR_MIN 53-55 = Max. write current at Vcc min */
+
+#define MMCSD_CSD_VDDWCURRMIN(csd) ((csd[9] >> 5) & 7)
+#define SD20_CSD_VDDWCURRMIN(csd) (7)
+
+/* VDD_W_CURR_MAX 50-52 = Max. write current at Vcc max */
+
+#define MMCSD_CSD_VDDWCURRMAX(csd) ((csd[9] >> 2) & 7)
+#define SD20_CSD_VDDWCURRMAX(csd) (6)
+
+/* C_SIZE_MULT 47-49 Device size multiplier */
+
+#define MMCSD_CSD_CSIZEMULT(csd) (((csd[9] & 3) << 1) | (csd[10] >> 7))
+#define SD20_CSD_CSIZEMULT(csd) (10-2)
+
+/* ER_BLK_EN 46-46 = Erase single block enable (SD) */
+
+#define SD_CSD_SDERBLKEN(csd) ((csd[10] >> 6) & 1)
+#define SD20_CSD_SDERBLKEN(csd) (1)
+
+/* SECTOR_SIZE 39-45 = Erase sector size (SD) */
+
+#define SD_CSD_SECTORSIZE(csd) (((csd[10] & 0x3f) << 1) | (csd[11] >> 7))
+#define SD20_CSD_SECTORSIZE(csd) (0x7f)
+
+/* SECTOR_SIZE 42-46 = Erase sector size (MMC) */
+
+#define MMC_CSD_SECTORSIZE(csd) ((csd[10] >> 2) & 0x1f)
+
+/* ER_GRP_SIZE 37-41 = Erase group size (MMC)*/
+
+#define MMC_CSD_ERGRPSIZE(csd) (((csd[10] & 3) << 3) | (csd[11] > 5))
+
+/* WP_GRP_SIZE 32-38 = Write protect group size (SD) */
+
+#define SD_CSD_WPGRPSIZE(csd) (csd[11] & 0x7f)
+#define SD20_CSD_WPGRPSIZE(csd) (0)
+
+/* WP_GRP_SIZE 32-36 = Write protect group size (MMC) */
+
+#define MMC_CSD_WPGRPSIZE(csd) (csd[11] & 0x1f)
+
+/* WP_GRP_EN 31-31 = Write protect group enable */
+
+#define MMCSD_WPGRPEN(csd) (csd[12] >> 7)
+#define SD20_WPGRPEN(csd) (0)
+
+/* DFLT_ECC 29-30 = Manufacturer default ECC (MMC) */
+
+#define MMC_CSD_DFLTECC(csd) ((csd[12] >> 5) & 3)
+
+/* R2W_FACTOR 26-28 = Write speed factor */
+
+#define MMCSD_CSD_R2WFACTOR(csd) ((csd[12] >> 2) & 7)
+#define SD20_CSD_R2WFACTOR(csd) (2)
+
+/* WRITE_BL_LEN 22-25 = Max. write data block length */
+
+#define MMCSD_CSD_WRITEBLLEN(csd) (((csd[12] & 3) << 2) | (csd[13] >> 6))
+#define SD20_CSD_WRITEBLLEN(csd) (9)
+
+/* WRITE_BL_PARTIAL 21-21 = Partial blocks for write allowed */
+
+#define MMCSD_CSD_WRITEBLPARTIAL(csd) ((csd[13] >> 5) & 1)
+#define SD20_CSD_WRITEBLPARTIAL(csd) (0)
+
+/* Reserved 16-20 */
+
+/* FILE_FORMAT_GROUP 15-15 = File format group */
+
+#define MMCSD_CSD_FILEFORMATGRP(csd) (csd[14] >> 7)
+#define SD20_CSD_FILEFORMATGRP(csd) (0)
+
+/* COPY 14-14 = Copy flag (OTP) */
+
+#define MMCSD_CSD_COPY(csd) ((csd[14] >> 6) & 1)
+#define SD20_CSD_COPY(csd) MMCSD_CSD_COPY(csd)
+
+/* PERM_WRITE_PROTECT 13-13 = Permanent write protection */
+
+#define MMCSD_CSD_PERMWRITEPROTECT(csd) ((csd[14] >> 5) & 1)
+#define SD20_CSD_PERMWRITEPROTECT(csd) MMCSD_CSD_PERMWRITEPROTECT(csd)
+
+/* TMP_WRITE_PROTECT 12-12 = Temporary write protection */
+
+#define MMCSD_CSD_TMPWRITEPROTECT(csd) ((csd[14] >> 4) & 1)
+#define SD20_CSD_TMPWRITEPROTECT(csd) MMCSD_CSD_TMPWRITEPROTECT(csd)
+
+/* FILE_FORMAT 10-11 = File format */
+
+#define MMCSD_CSD_FILEFORMAT(csd) ((csd[14] >> 2) & 3)
+#define SD20_CSD_FILEFORMAT(csd) (0)
+
+/* ECC 8-9 = ECC (MMC) */
+
+#define MMC_CSD_ECC(csd) (csd[14] & 3)
+
+/* CRC 1-7 = CRC */
+
+#define MMCSD_CSD_CRC(csd) (csd[15] >> 1)
+#define SD20_CSD_CRC(csd) MMCSD_CSD_CRC(csd)
+
+/* Reserved 0-0 */
+
+#endif /* CONFIG_MMCSD_BE16 */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+#endif /* __DRIVERS_MMCSD_MMCSD_CSD_H */
diff --git a/nuttx/drivers/mmcsd/mmcsd_debug.c b/nuttx/drivers/mmcsd/mmcsd_debug.c
new file mode 100644
index 000000000..0bd7f896e
--- /dev/null
+++ b/nuttx/drivers/mmcsd/mmcsd_debug.c
@@ -0,0 +1,183 @@
+/****************************************************************************
+ * drivers/mmcsd/mmcsd_debug.c
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include "mmcsd_csd.h"
+#include "mmcsd_internal.h"
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+/* This needs to match the logic in include/debug.h */
+
+#ifdef CONFIG_CPP_HAVE_VARARGS
+# define message(format, arg...) lib_rawprintf(format, ##arg)
+#else
+# define message lib_rawprintf
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mmcsd_dmpcsd
+ *
+ * Description:
+ * Dump the contents of the CSD
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+void mmcsd_dmpcsd(FAR const uint8_t *csd, uint8_t cardtype)
+{
+ bool mmc = (cardtype == MMCSD_CARDTYPE_MMC);
+ bool sd2 = (MMCSD_CSD_CSDSTRUCT(csd) == 1);
+
+ fvdbg("CSD\n");
+ fvdbg(" CSD_STRUCTURE: 1.%d\n", MMCSD_CSD_CSDSTRUCT(csd));
+ if (mmc)
+ {
+ fvdbg(" MMC SPEC_VERS: %d\n", MMC_CSD_SPECVERS(csd));
+ }
+ fvdbg(" TAAC:\n",
+ sd2 ? SD20_CSD_TAC_TIMEVALUE(csd) : MMCSD_CSD_TAAC_TIMEVALUE(csd));
+ fvdbg(" TIME_VALUE: 0x%02x\n",
+ sd2 ? SD20_CSD_TAC_TIMEVALUE(csd) : MMCSD_CSD_TAAC_TIMEVALUE(csd));
+ fvdbg(" TIME_UNIT: 0x%02x\n",
+ sd2 ? SD20_CSD_TAC_TIMEUNIT(csd) : MMCSD_CSD_TAAC_TIMEUNIT(csd));
+ fvdbg(" NSAC: 0x%02x\n",
+ sd2 ? SD20_CSD_NSAC(csd) : MMCSD_CSD_NSAC(csd));
+ fvdbg(" TRAN_SPEED:\n");
+ fvdbg(" TIME_VALUE: 0x%02x\n",
+ sd2 ? SD20_CSD_TRANSPEED_TIMEVALUE(csd) : MMCSD_CSD_TRANSPEED_TIMEVALUE(csd));
+ fvdbg(" RATE_UNIT: 0x%02x\n",
+ sd2 ? SD20_CSD_TRANSPEED_TRANSFERRATEUNIT(csd) : MMCSD_CSD_TRANSPEED_TRANSFERRATEUNIT(csd));
+ fvdbg(" CCC: 0x%03x\n",
+ sd2 ? SD20_CSD_CCC(csd) : MMCSD_CSD_CCC(csd));
+ fvdbg(" READ_BL_LEN: %d\n",
+ sd2 ? SD20_CSD_READBLLEN(csd) : MMCSD_CSD_READBLLEN(csd));
+ fvdbg(" READ_BL_PARTIAL: %d\n",
+ sd2 ? SD20_CSD_READBLPARTIAL(csd) : MMCSD_CSD_READBLPARTIAL(csd));
+ fvdbg(" WRITE_BLK_MISALIGN: %d\n",
+ sd2 ? SD20_CSD_WRITEBLKMISALIGN(csd) : MMCSD_CSD_WRITEBLKMISALIGN(csd));
+ fvdbg(" READ_BLK_MISALIGN: %d\n",
+ sd2 ? SD20_CSD_READBLKMISALIGN(csd) : MMCSD_CSD_READBLKMISALIGN(csd));
+ fvdbg(" DSR_IMP: %d\n",
+ sd2 ? SD20_CSD_DSRIMP(csd) : MMCSD_CSD_DSRIMP(csd));
+ fvdbg(" C_SIZE: %d\n",
+ sd2 ? SD20_CSD_CSIZE(csd) : MMCSD_CSD_CSIZE(csd));
+ fvdbg(" VDD_R_CURR_MIN: %d\n",
+ sd2 ? SD20_CSD_VDDRCURRMIN(csd) : MMCSD_CSD_VDDRCURRMIN(csd));
+ fvdbg(" VDD_R_CURR_MAX: %d\n",
+ sd2 ? SD20_CSD_VDDRCURRMAX(csd) : MMCSD_CSD_VDDRCURRMAX(csd));
+ fvdbg(" VDD_W_CURR_MIN: %d\n",
+ sd2 ? SD20_CSD_VDDWCURRMIN(csd) : MMCSD_CSD_VDDWCURRMIN(csd));
+ fvdbg(" VDD_W_CURR_MAX: %d\n",
+ sd2 ? SD20_CSD_VDDWCURRMAX(csd) : MMCSD_CSD_VDDWCURRMAX(csd));
+ fvdbg(" C_SIZE_MULT: %d\n",
+ sd2 ? SD20_CSD_CSIZEMULT(csd) : MMCSD_CSD_CSIZEMULT(csd));
+ if (mmc)
+ {
+ fvdbg(" MMC SECTOR_SIZE: %d\n", MMC_CSD_SECTORSIZE(csd));
+ fvdbg(" MMC ER_GRP_SIZE: %d\n", MMC_CSD_ERGRPSIZE(csd));
+ fvdbg(" MMC WP_GRP_SIZE: %d\n", MMC_CSD_WPGRPSIZE(csd));
+ fvdbg(" MMC DFLT_ECC: %d\n", MMC_CSD_DFLTECC(csd));
+ }
+ else
+ {
+ fvdbg(" SD ER_BLK_EN: %d\n",
+ sd2 ? SD20_CSD_SDERBLKEN(csd) : SD_CSD_SDERBLKEN(csd));
+ fvdbg(" SD SECTOR_SIZE: %d\n",
+ sd2 ? SD20_CSD_SECTORSIZE(csd) : SD_CSD_SECTORSIZE(csd));
+ fvdbg(" SD WP_GRP_SIZE: %d\n",
+ sd2 ? SD_CSD_WPGRPSIZE(csd) : SD_CSD_WPGRPSIZE(csd));
+ }
+ fvdbg(" WP_GRP_EN: %d\n",
+ sd2 ? SD20_WPGRPEN(csd) : MMCSD_WPGRPEN(csd));
+ fvdbg(" R2W_FACTOR: %d\n",
+ sd2 ? SD20_CSD_R2WFACTOR(csd) : MMCSD_CSD_R2WFACTOR(csd));
+ fvdbg(" WRITE_BL_LEN: %d\n",
+ sd2 ? SD20_CSD_WRITEBLLEN(csd) : MMCSD_CSD_WRITEBLLEN(csd));
+ fvdbg(" WRITE_BL_PARTIAL: %d\n",
+ sd2 ? SD20_CSD_WRITEBLPARTIAL(csd) : MMCSD_CSD_WRITEBLPARTIAL(csd));
+ fvdbg(" FILE_FORMAT_GROUP: %d\n",
+ sd2 ? SD20_CSD_FILEFORMATGRP(csd) : MMCSD_CSD_FILEFORMATGRP(csd));
+ fvdbg(" COPY: %d\n",
+ sd2 ? SD20_CSD_COPY(csd) : MMCSD_CSD_COPY(csd));
+ fvdbg(" PERM_WRITE_PROTECT: %d\n",
+ sd2 ? SD20_CSD_PERMWRITEPROTECT(csd) : MMCSD_CSD_PERMWRITEPROTECT(csd));
+ fvdbg(" TMP_WRITE_PROTECT: %d\n",
+ sd2 ?SD20_CSD_TMPWRITEPROTECT(csd) : MMCSD_CSD_TMPWRITEPROTECT(csd));
+ fvdbg(" FILE_FORMAT: %d\n",
+ sd2 ? SD20_CSD_FILEFORMAT(csd) : MMCSD_CSD_FILEFORMAT(csd));
+ if (mmc)
+ {
+ fvdbg(" MMC ECC: %d\n",
+ sd2 ? MMC_CSD_ECC(csd) : MMC_CSD_ECC(csd));
+ }
+ fvdbg(" CRC: %02x\n",
+ sd2 ? SD20_CSD_CRC(csd) : MMCSD_CSD_CRC(csd));
+}
+#endif
diff --git a/nuttx/drivers/mmcsd/mmcsd_internal.h b/nuttx/drivers/mmcsd/mmcsd_internal.h
new file mode 100644
index 000000000..ed669cdfa
--- /dev/null
+++ b/nuttx/drivers/mmcsd/mmcsd_internal.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * drivers/mmcsd/mmcsd_internal.h
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_MMCSD_MMCSD_INTERNAL_H
+#define __DRIVERS_MMCSD_MMCSD_INTERNAL_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+#include <debug.h>
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+/* Enable excessive debug options */
+
+#undef CONFIG_MMCSD_DUMPALL /* MUST BE DEFINED MANUALLY */
+
+#if !defined(CONFIG_DEBUG_VERBOSE) || !defined(CONFIG_DEBUG_FS)
+# undef CONFIG_MMCSD_DUMPALL
+#endif
+
+/* Card type */
+
+#define MMCSD_CARDTYPE_UNKNOWN 0 /* Unknown card type */
+#define MMCSD_CARDTYPE_MMC 1 /* Bit 0: MMC card */
+#define MMCSD_CARDTYPE_SDV1 2 /* Bit 1: SD version 1.x */
+#define MMCSD_CARDTYPE_SDV2 4 /* Bit 2: SD version 2.x with byte addressing */
+#define MMCSD_CARDTYPE_BLOCK 8 /* Bit 3: SD version 2.x with block addressing */
+
+#define IS_MMC(t) (((t) & MMCSD_CARDTYPE_MMC) != 0)
+#define IS_SD(t) (((t) & (MMCSD_CARDTYPE_SDV1|MMCSD_CARDTYPE_SDV2)) != 0)
+#define IS_SDV1(t) (((t) & MMCSD_CARDTYPE_SDV1) != 0)
+#define IS_SDV2(t) (((t) & MMCSD_CARDTYPE_SDV2) != 0)
+#define IS_BLOCK(t) (((t) & MMCSD_CARDTYPE_BLOCK) != 0)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+#ifdef CONFIG_MMCSD_DUMPALL
+# define mmcsd_dumpbuffer(m,b,l) fvdbgdumpbuffer(m,b,l)
+#else
+# define mmcsd_dumpbuffer(m,b,l)
+#endif
+
+#if defined(CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+EXTERN void mmcsd_dmpcsd(FAR const uint8_t *csd, uint8_t cardtype);
+#else
+# define mmcsd_dmpcsd(csd,cadtype)
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+#endif /* __DRIVERS_MMCSD_MMCSD_INTERNAL_H */
diff --git a/nuttx/drivers/mmcsd/mmcsd_sdio.c b/nuttx/drivers/mmcsd/mmcsd_sdio.c
new file mode 100644
index 000000000..d0bc6659c
--- /dev/null
+++ b/nuttx/drivers/mmcsd/mmcsd_sdio.c
@@ -0,0 +1,3180 @@
+/****************************************************************************
+ * drivers/mmcsd/mmcsd_sdio.c
+ *
+ * Copyright (C) 2009-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <semaphore.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/clock.h>
+#include <nuttx/arch.h>
+#include <nuttx/rwbuffer.h>
+#include <nuttx/sdio.h>
+#include <nuttx/mmcsd.h>
+
+#include "mmcsd_internal.h"
+#include "mmcsd_sdio.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The maximum number of references on the driver (because a uint8_t is used.
+ * Use a larger type if more references are needed.
+ */
+
+#define MAX_CREFS 0xff
+
+/* Timing (all in units of microseconds) */
+
+#define MMCSD_POWERUP_DELAY ((useconds_t)250) /* 74 clock cycles @ 400KHz = 185uS */
+#define MMCSD_IDLE_DELAY ((useconds_t)50000) /* Short delay to allow change to IDLE state */
+#define MMCSD_DSR_DELAY ((useconds_t)100000) /* Time to wait after setting DSR */
+#define MMCSD_CLK_DELAY ((useconds_t)500000) /* Delay after changing clock speeds */
+
+/* Data delays (all in units of milliseconds).
+ *
+ * For MMC & SD V1.x, these should be based on Nac = TAAC + NSAC; The maximum
+ * value of TAAC is 80MS and the maximum value of NSAC is 25.5K clock cycle.
+ * For SD V2.x, a fixed delay of 100MS is recommend which is pretty close to
+ * the worst case SD V1.x Nac. Here we just use 100MS delay for all data
+ * transfers.
+ */
+
+#define MMCSD_SCR_DATADELAY (100) /* Wait up to 100MS to get SCR */
+#define MMCSD_BLOCK_DATADELAY (100) /* Wait up to 100MS to get one data block */
+
+#define IS_EMPTY(priv) (priv->type == MMCSD_CARDTYPE_UNKNOWN)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure is contains the unique state of the MMC/SD block driver */
+
+struct mmcsd_state_s
+{
+ FAR struct sdio_dev_s *dev; /* The SDIO device bound to this instance */
+ uint8_t crefs; /* Open references on the driver */
+ sem_t sem; /* Assures mutually exclusive access to the slot */
+
+ /* Status flags */
+
+ uint8_t probed:1; /* true: mmcsd_probe() discovered a card */
+ uint8_t widebus:1; /* true: Wide 4-bit bus selected */
+ uint8_t mediachanged:1; /* true: Media changed since last check */
+ uint8_t wrbusy:1; /* true: Last transfer was a write, card may be busy */
+ uint8_t wrprotect:1; /* true: Card is write protected (from CSD) */
+ uint8_t locked:1; /* true: Media is locked (from R1) */
+ uint8_t dsrimp:1; /* true: card supports CMD4/DSR setting (from CSD) */
+#ifdef CONFIG_SDIO_DMA
+ uint8_t dma:1; /* true: hardware supports DMA */
+#endif
+
+ uint8_t mode:2; /* (See MMCSDMODE_* definitions) */
+ uint8_t type:4; /* Card type (See MMCSD_CARDTYPE_* definitions) */
+ uint8_t buswidth:4; /* Bus widthes supported (SD only) */
+ uint16_t selblocklen; /* The currently selected block length */
+ uint16_t rca; /* Relative Card Address (RCS) register */
+
+ /* Memory card geometry (extracted from the CSD) */
+
+ uint8_t blockshift; /* Log2 of blocksize */
+ uint16_t blocksize; /* Read block length (== block size) */
+ uint32_t nblocks; /* Number of blocks */
+
+#ifdef CONFIG_HAVE_LONG_LONG
+ uint64_t capacity; /* Total capacity of volume */
+#else
+ uint32_t capacity; /* Total capacity of volume (Limited to 4Gb) */
+#endif
+ /* Read-ahead and write buffering support */
+
+#if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD)
+ struct rwbuffer_s rwbuffer;
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Misc Helpers *************************************************************/
+
+static void mmcsd_takesem(FAR struct mmcsd_state_s *priv);
+
+#ifndef CONFIG_SDIO_MUXBUS
+# define mmcsd_givesem(p) sem_post(&priv->sem);
+#endif
+
+/* Command/response helpers *************************************************/
+
+static int mmcsd_sendcmdpoll(FAR struct mmcsd_state_s *priv,
+ uint32_t cmd, uint32_t arg);
+static int mmcsd_recvR1(FAR struct mmcsd_state_s *priv, uint32_t cmd);
+static int mmcsd_recvR6(FAR struct mmcsd_state_s *priv, uint32_t cmd);
+static int mmcsd_getSCR(FAR struct mmcsd_state_s *priv, uint32_t scr[2]);
+
+static void mmcsd_decodeCSD(FAR struct mmcsd_state_s *priv,
+ uint32_t csd[4]);
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+static void mmcsd_decodeCID(FAR struct mmcsd_state_s *priv,
+ uint32_t cid[4]);
+#else
+# define mmcsd_decodeCID(priv,cid)
+#endif
+static void mmcsd_decodeSCR(FAR struct mmcsd_state_s *priv,
+ uint32_t scr[2]);
+
+static int mmcsd_getR1(FAR struct mmcsd_state_s *priv, FAR uint32_t *r1);
+static int mmcsd_verifystate(FAR struct mmcsd_state_s *priv,
+ uint32_t status);
+
+/* Transfer helpers *********************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static bool mmcsd_wrprotected(FAR struct mmcsd_state_s *priv);
+#endif
+static int mmcsd_eventwait(FAR struct mmcsd_state_s *priv,
+ sdio_eventset_t failevents, uint32_t timeout);
+static int mmcsd_transferready(FAR struct mmcsd_state_s *priv);
+#ifndef CONFIG_MMCSD_MULTIBLOCK_DISABLE
+static int mmcsd_stoptransmission(FAR struct mmcsd_state_s *priv);
+#endif
+static int mmcsd_setblocklen(FAR struct mmcsd_state_s *priv,
+ uint32_t blocklen);
+static ssize_t mmcsd_readsingle(FAR struct mmcsd_state_s *priv,
+ FAR uint8_t *buffer, off_t startblock);
+#ifndef CONFIG_MMCSD_MULTIBLOCK_DISABLE
+static ssize_t mmcsd_readmultiple(FAR struct mmcsd_state_s *priv,
+ FAR uint8_t *buffer, off_t startblock, size_t nblocks);
+#endif
+#ifdef CONFIG_FS_READAHEAD
+static ssize_t mmcsd_reload(FAR void *dev, FAR uint8_t *buffer,
+ off_t startblock, size_t nblocks);
+#endif
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t mmcsd_writesingle(FAR struct mmcsd_state_s *priv,
+ FAR const uint8_t *buffer, off_t startblock);
+#ifndef CONFIG_MMCSD_MULTIBLOCK_DISABLE
+static ssize_t mmcsd_writemultiple(FAR struct mmcsd_state_s *priv,
+ FAR const uint8_t *buffer, off_t startblock, size_t nblocks);
+#endif
+#ifdef CONFIG_FS_WRITEBUFFER
+static ssize_t mmcsd_flush(FAR void *dev, FAR const uint8_t *buffer,
+ off_t startblock, size_t nblocks);
+#endif
+#endif
+
+/* Block driver methods *****************************************************/
+
+static int mmcsd_open(FAR struct inode *inode);
+static int mmcsd_close(FAR struct inode *inode);
+static ssize_t mmcsd_read(FAR struct inode *inode, FAR unsigned char *buffer,
+ size_t startsector, unsigned int nsectors);
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t mmcsd_write(FAR struct inode *inode,
+ FAR const unsigned char *buffer, size_t startsector,
+ unsigned int nsectors);
+#endif
+static int mmcsd_geometry(FAR struct inode *inode,
+ FAR struct geometry *geometry);
+static int mmcsd_ioctl(FAR struct inode *inode, int cmd,
+ unsigned long arg);
+
+/* Initialization/uninitialization/reset ************************************/
+
+static void mmcsd_mediachange(FAR void *arg);
+static int mmcsd_widebus(FAR struct mmcsd_state_s *priv);
+#ifdef CONFIG_MMCSD_MMCSUPPORT
+static int mmcsd_mmcinitialize(FAR struct mmcsd_state_s *priv);
+#endif
+static int mmcsd_sdinitialize(FAR struct mmcsd_state_s *priv);
+static int mmcsd_cardidentify(FAR struct mmcsd_state_s *priv);
+static int mmcsd_probe(FAR struct mmcsd_state_s *priv);
+static int mmcsd_removed(FAR struct mmcsd_state_s *priv);
+static int mmcsd_hwinitialize(FAR struct mmcsd_state_s *priv);
+static void mmcsd_hwuninitialize(FAR struct mmcsd_state_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct block_operations g_bops =
+{
+ mmcsd_open, /* open */
+ mmcsd_close, /* close */
+ mmcsd_read, /* read */
+#ifdef CONFIG_FS_WRITABLE
+ mmcsd_write, /* write */
+#else
+ NULL, /* write */
+#endif
+ mmcsd_geometry, /* geometry */
+ mmcsd_ioctl /* ioctl */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Misc Helpers
+ ****************************************************************************/
+
+static void mmcsd_takesem(FAR struct mmcsd_state_s *priv)
+{
+ /* Take the semaphore, giving exclusive access to the driver (perhaps
+ * waiting)
+ */
+
+ while (sem_wait(&priv->sem) != 0)
+ {
+ /* The only case that an error should occur here is if the wait was
+ * awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+
+ /* Lock the bus if mutually exclusive access to the SDIO bus is required
+ * on this platform.
+ */
+
+#ifdef CONFIG_SDIO_MUXBUS
+ SDIO_LOCK(priv->dev, TRUE);
+#endif
+}
+
+#ifdef CONFIG_SDIO_MUXBUS
+static void mmcsd_givesem(FAR struct mmcsd_state_s *priv)
+{
+ /* Release the SDIO bus lock, then the MMC/SD driver semaphore in the
+ * opposite order that they were taken to assure that no deadlock
+ * conditions will arise.
+ */
+
+ SDIO_LOCK(priv->dev, FALSE);
+ sem_post(&priv->sem);
+}
+#endif
+
+/****************************************************************************
+ * Command/Response Helpers
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mmcsd_sendcmdpoll
+ *
+ * Description:
+ * Send a command and poll-wait for the response.
+ *
+ ****************************************************************************/
+
+static int mmcsd_sendcmdpoll(FAR struct mmcsd_state_s *priv, uint32_t cmd,
+ uint32_t arg)
+{
+ int ret;
+
+ /* Send the command */
+
+ ret = SDIO_SENDCMD(priv->dev, cmd, arg);
+ if (ret == OK)
+ {
+ /* Then poll-wait until the response is available */
+
+ ret = SDIO_WAITRESPONSE(priv->dev, cmd);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Wait for response to cmd: %08x failed: %d\n", cmd, ret);
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_sendcmd4
+ *
+ * Description:
+ * Set the Driver Stage Register (DSR) if (1) a CONFIG_MMCSD_DSR has been
+ * provided and (2) the card supports a DSR register. If no DSR value
+ * the card default value (0x0404) will be used.
+ *
+ ****************************************************************************/
+
+static inline int mmcsd_sendcmd4(FAR struct mmcsd_state_s *priv)
+{
+ int ret = OK;
+
+#ifdef CONFIG_MMCSD_DSR
+ /* The dsr_imp bit from the CSD will tell us if the card supports setting
+ * the DSR via CMD4 or not.
+ */
+
+ if (priv->dsrimp != false)
+ {
+ /* CMD4 = SET_DSR will set the cards DSR register. The DSR and CMD4
+ * support are optional. However, since this is a broadcast command
+ * with no response (like CMD0), we will never know if the DSR was
+ * set correctly or not
+ */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD4, CONFIG_MMCSD_DSR << 16);
+ up_udelay(MMCSD_DSR_DELAY);
+
+ /* Send it again to have more confidence */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD4, CONFIG_MMCSD_DSR << 16);
+ up_udelay(MMCSD_DSR_DELAY);
+ }
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_recvR1
+ *
+ * Description:
+ * Receive R1 response and check for errors.
+ *
+ ****************************************************************************/
+
+static int mmcsd_recvR1(FAR struct mmcsd_state_s *priv, uint32_t cmd)
+{
+ uint32_t r1;
+ int ret;
+
+ /* Get the R1 response from the hardware */
+
+ ret = SDIO_RECVR1(priv->dev, cmd, &r1);
+ if (ret == OK)
+ {
+ /* Check if R1 reports an error */
+
+ if ((r1 & MMCSD_R1_ERRORMASK) != 0)
+ {
+ /* Card locked is considered an error. Save the card locked
+ * indication for later use.
+ */
+
+ fvdbg("ERROR: R1=%08x\n", r1);
+ priv->locked = ((r1 & MMCSD_R1_CARDISLOCKED) != 0);
+ ret = -EIO;
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_recvR6
+ *
+ * Description:
+ * Receive R6 response and check for errors. On success, priv->rca is set
+ * to the received RCA
+ *
+ ****************************************************************************/
+
+static int mmcsd_recvR6(FAR struct mmcsd_state_s *priv, uint32_t cmd)
+{
+ uint32_t r6 = 0;
+ int ret;
+
+ /* R6 Published RCA Response (48-bit, SD card only)
+ * 47 0 Start bit
+ * 46 0 Transmission bit (0=from card)
+ * 45:40 bit5 - bit0 Command index (0-63)
+ * 39:8 bit31 - bit0 32-bit Argument Field, consisting of:
+ * [31:16] New published RCA of card
+ * [15:0] Card status bits {23,22,19,12:0}
+ * 7:1 bit6 - bit0 CRC7
+ * 0 1 End bit
+ *
+ * Get the R1 response from the hardware
+ */
+
+ ret = SDIO_RECVR6(priv->dev, cmd, &r6);
+ if (ret == OK)
+ {
+ /* Check if R6 reports an error */
+
+ if ((r6 & MMCSD_R6_ERRORMASK) == 0)
+ {
+ /* No, save the RCA and return success */
+
+ priv->rca = (uint16_t)(r6 >> 16);
+ return OK;
+ }
+
+ /* Otherwise, return an I/O failure */
+
+ ret = -EIO;
+ }
+
+ fdbg("ERROR: Failed to get RCA. R6=%08x: %d\n", r6, ret);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_getSCR
+ *
+ * Description:
+ * Obtain the SD card's Configuration Register (SCR)
+ *
+ * Returned Value:
+ * OK on success; a negated ernno on failure.
+ *
+ ****************************************************************************/
+
+static int mmcsd_getSCR(FAR struct mmcsd_state_s *priv, uint32_t scr[2])
+{
+ int ret;
+
+ /* Set Block Size To 8 Bytes */
+
+ ret = mmcsd_setblocklen(priv, 8);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_setblocklen failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Send CMD55 APP_CMD with argument as card's RCA */
+
+ mmcsd_sendcmdpoll(priv, SD_CMD55, (uint32_t)priv->rca << 16);
+ ret = mmcsd_recvR1(priv, SD_CMD55);
+ if (ret != OK)
+ {
+ fdbg("ERROR: RECVR1 for CMD55 failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Setup up to receive data with interrupt mode */
+
+ SDIO_BLOCKSETUP(priv->dev, 8, 1);
+ SDIO_RECVSETUP(priv->dev, (FAR uint8_t*)scr, 8);
+
+ /* Send ACMD51 SD_APP_SEND_SCR with argument as 0 to start data receipt */
+
+ (void)SDIO_WAITENABLE(priv->dev, SDIOWAIT_TRANSFERDONE|SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR);
+ mmcsd_sendcmdpoll(priv, SD_ACMD51, 0);
+ ret = mmcsd_recvR1(priv, SD_ACMD51);
+ if (ret != OK)
+ {
+ fdbg("ERROR: RECVR1 for ACMD51 failed: %d\n", ret);
+ SDIO_CANCEL(priv->dev);
+ return ret;
+ }
+
+ /* Wait for data to be transferred */
+
+ ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR, MMCSD_SCR_DATADELAY);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_eventwait for READ DATA failed: %d\n", ret);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_decodeCSD
+ *
+ * Description:
+ * Decode and extract necessary information from the CSD. If debug is
+ * enabled, then decode and show the full contents of the CSD.
+ *
+ * Returned Value:
+ * OK on success; a negated ernno on failure. On success, the following
+ * values will be set in the driver state structure:
+ *
+ * priv->dsrimp true: card supports CMD4/DSR setting (from CSD)
+ * priv->wrprotect true: card is write protected (from CSD)
+ * priv->blocksize Read block length (== block size)
+ * priv->nblocks Number of blocks
+ * priv->capacity Total capacity of volume
+ *
+ ****************************************************************************/
+
+static void mmcsd_decodeCSD(FAR struct mmcsd_state_s *priv, uint32_t csd[4])
+{
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+ struct mmcsd_csd_s decoded;
+#endif
+ unsigned int readbllen;
+ bool permwriteprotect;
+ bool tmpwriteprotect;
+
+ /* Word 1: Bits 127-96:
+ *
+ * CSD_STRUCTURE 127:126 CSD structure
+ * SPEC_VERS 125:122 (MMC) Spec version
+ * TAAC 119:112 Data read access-time-1
+ * TIME_VALUE 6:3 Time mantissa
+ * TIME_UNIT 2:0 Time exponent
+ * NSAC 111:104 Data read access-time-2 in CLK cycle(NSAC*100)
+ * TRAN_SPEED 103:96 Max. data transfer rate
+ * TIME_VALUE 6:3 Rate exponent
+ * TRANSFER_RATE_UNIT 2:0 Rate mantissa
+ */
+
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+ memset(&decoded, 0, sizeof(struct mmcsd_csd_s));
+ decoded.csdstructure = csd[0] >> 30;
+ decoded.mmcspecvers = (csd[0] >> 26) & 0x0f;
+ decoded.taac.timevalue = (csd[0] >> 19) & 0x0f;
+ decoded.taac.timeunit = (csd[0] >> 16) & 7;
+ decoded.nsac = (csd[0] >> 8) & 0xff;
+ decoded.transpeed.timevalue = (csd[0] >> 3) & 0x0f;
+ decoded.transpeed.transferrateunit = csd[0] & 7;
+#endif
+
+ /* Word 2: Bits 64:95
+ * CCC 95:84 Card command classes
+ * READ_BL_LEN 83:80 Max. read data block length
+ * READ_BL_PARTIAL 79:79 Partial blocks for read allowed
+ * WRITE_BLK_MISALIGN 78:78 Write block misalignment
+ * READ_BLK_MISALIGN 77:77 Read block misalignment
+ * DSR_IMP 76:76 DSR implemented
+ * Byte addressed SD and MMC:
+ * C_SIZE 73:62 Device size
+ * Block addressed SD:
+ * 75:70 (reserved)
+ * C_SIZE 48:69 Device size
+ */
+
+ priv->dsrimp = (csd[1] >> 12) & 1;
+ readbllen = (csd[1] >> 16) & 0x0f;
+
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+ decoded.ccc = (csd[1] >> 20) & 0x0fff;
+ decoded.readbllen = (csd[1] >> 16) & 0x0f;
+ decoded.readblpartial = (csd[1] >> 15) & 1;
+ decoded.writeblkmisalign = (csd[1] >> 14) & 1;
+ decoded.readblkmisalign = (csd[1] >> 13) & 1;
+ decoded.dsrimp = priv->dsrimp;
+#endif
+
+ /* Word 3: Bits 32-63
+ *
+ * Byte addressed SD:
+ * C_SIZE 73:62 Device size
+ * VDD_R_CURR_MIN 61:59 Max. read current at Vcc min
+ * VDD_R_CURR_MAX 58:56 Max. read current at Vcc max
+ * VDD_W_CURR_MIN 55:53 Max. write current at Vcc min
+ * VDD_W_CURR_MAX 52:50 Max. write current at Vcc max
+ * C_SIZE_MULT 49:47 Device size multiplier
+ * SD_ER_BLK_EN 46:46 Erase single block enable (SD only)
+ * SD_SECTOR_SIZE 45:39 Erase sector size
+ * SD_WP_GRP_SIZE 38:32 Write protect group size
+ * Block addressed SD:
+ * 75:70 (reserved)
+ * C_SIZE 48:69 Device size
+ * 47:47 (reserved)
+ * SD_ER_BLK_EN 46:46 Erase single block enable (SD only)
+ * SD_SECTOR_SIZE 45:39 Erase sector size
+ * SD_WP_GRP_SIZE 38:32 Write protect group size
+ * MMC:
+ * C_SIZE 73:62 Device size
+ * VDD_R_CURR_MIN 61:59 Max. read current at Vcc min
+ * VDD_R_CURR_MAX 58:56 Max. read current at Vcc max
+ * VDD_W_CURR_MIN 55:53 Max. write current at Vcc min
+ * VDD_W_CURR_MAX 52:50 Max. write current at Vcc max
+ * C_SIZE_MULT 49:47 Device size multiplier
+ * MMC_SECTOR_SIZE 46:42 Erase sector size
+ * MMC_ER_GRP_SIZE 41:37 Erase group size (MMC)
+ * MMC_WP_GRP_SIZE 36:32 Write protect group size
+ */
+
+ if (IS_BLOCK(priv->type))
+ {
+ /* Block addressed SD:
+ *
+ * C_SIZE: 69:64 from Word 2 and 63:48 from Word 3
+ *
+ * 512 = (1 << 9)
+ * 1024 = (1 << 10)
+ * 512*1024 = (1 << 19)
+ */
+
+ uint32_t csize = ((csd[1] & 0x3f) << 16) | (csd[2] >> 16);
+#ifdef CONFIG_HAVE_LONG_LONG
+ priv->capacity = ((uint64_t)(csize + 1)) << 19;
+#else
+ priv->capacity = (csize + 1) << 19;
+#endif
+ priv->blockshift = 9;
+ priv->blocksize = 1 << 9;
+ priv->nblocks = priv->capacity >> 9;
+
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+ decoded.u.sdblock.csize = csize;
+ decoded.u.sdblock.sderblen = (csd[2] >> 14) & 1;
+ decoded.u.sdblock.sdsectorsize = (csd[2] >> 7) & 0x7f;
+ decoded.u.sdblock.sdwpgrpsize = csd[2] & 0x7f;
+#endif
+ }
+ else
+ {
+ /* Byte addressed SD:
+ *
+ * C_SIZE: 73:64 from Word 2 and 63:62 from Word 3
+ */
+
+ uint16_t csize = ((csd[1] & 0x03ff) << 2) | ((csd[2] >> 30) & 3);
+ uint8_t csizemult = (csd[2] >> 15) & 7;
+
+ priv->nblocks = ((uint32_t)csize + 1) * (1 << (csizemult + 2));
+ priv->blockshift = readbllen;
+ priv->blocksize = (1 << readbllen);
+ priv->capacity = (priv->nblocks << readbllen);
+
+ /* Some devices, such as 2Gb devices, report blocksizes larger than 512 bytes
+ * but still expect to be accessed with a 512 byte blocksize.
+ *
+ * NOTE: A minor optimization would be to eliminated priv->blocksize and
+ * priv->blockshift: Those values will be 512 and 9 in all cases anyway.
+ */
+
+ if (priv->blocksize > 512)
+ {
+ priv->nblocks <<= (priv->blockshift - 9);
+ priv->blocksize = 512;
+ priv->blockshift = 9;
+ }
+
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+ if (IS_SD(priv->type))
+ {
+ decoded.u.sdbyte.csize = csize;
+ decoded.u.sdbyte.vddrcurrmin = (csd[2] >> 27) & 7;
+ decoded.u.sdbyte.vddrcurrmax = (csd[2] >> 24) & 7;
+ decoded.u.sdbyte.vddwcurrmin = (csd[2] >> 21) & 7;
+ decoded.u.sdbyte.vddwcurrmax = (csd[2] >> 18) & 7;
+ decoded.u.sdbyte.csizemult = csizemult;
+ decoded.u.sdbyte.sderblen = (csd[2] >> 14) & 1;
+ decoded.u.sdbyte.sdsectorsize = (csd[2] >> 7) & 0x7f;
+ decoded.u.sdbyte.sdwpgrpsize = csd[2] & 0x7f;
+ }
+#ifdef CONFIG_MMCSD_MMCSUPPORT
+ else if (IS_MMC(priv->type))
+ {
+ decoded.u.mmc.csize = csize;
+ decoded.u.mmc.vddrcurrmin = (csd[2] >> 27) & 7;
+ decoded.u.mmc.vddrcurrmax = (csd[2] >> 24) & 7;
+ decoded.u.mmc.vddwcurrmin = (csd[2] >> 21) & 7;
+ decoded.u.mmc.vddwcurrmax = (csd[2] >> 18) & 7;
+ decoded.u.mmc.csizemult = csizemult;
+ decoded.u.mmc.er.mmc22.sectorsize = (csd[2] >> 10) & 0x1f;
+ decoded.u.mmc.er.mmc22.ergrpsize = (csd[2] >> 5) & 0x1f;
+ decoded.u.mmc.mmcwpgrpsize = csd[2] & 0x1f;
+ }
+#endif
+#endif
+ }
+
+ /* Word 4: Bits 0-31
+ * WP_GRP_EN 31:31 Write protect group enable
+ * MMC DFLT_ECC 30:29 Manufacturer default ECC (MMC only)
+ * R2W_FACTOR 28:26 Write speed factor
+ * WRITE_BL_LEN 25:22 Max. write data block length
+ * WRITE_BL_PARTIAL 21:21 Partial blocks for write allowed
+ * FILE_FORMAT_GROUP 15:15 File format group
+ * COPY 14:14 Copy flag (OTP)
+ * PERM_WRITE_PROTECT 13:13 Permanent write protection
+ * TMP_WRITE_PROTECT 12:12 Temporary write protection
+ * FILE_FORMAT 10:11 File format
+ * ECC 9:8 ECC (MMC only)
+ * CRC 7:1 CRC
+ * Not used 0:0
+ */
+
+ permwriteprotect = (csd[3] >> 13) & 1;
+ tmpwriteprotect = (csd[3] >> 12) & 1;
+ priv->wrprotect = (permwriteprotect || tmpwriteprotect);
+
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+ decoded.wpgrpen = csd[3] >> 31;
+ decoded.mmcdfltecc = (csd[3] >> 29) & 3;
+ decoded.r2wfactor = (csd[3] >> 26) & 7;
+ decoded.writebllen = (csd[3] >> 22) & 0x0f;
+ decoded.writeblpartial = (csd[3] >> 21) & 1;
+ decoded.fileformatgrp = (csd[3] >> 15) & 1;
+ decoded.copy = (csd[3] >> 14) & 1;
+ decoded.permwriteprotect = permwriteprotect;
+ decoded.tmpwriteprotect = tmpwriteprotect;
+ decoded.fileformat = (csd[3] >> 10) & 3;
+ decoded.mmcecc = (csd[3] >> 8) & 3;
+ decoded.crc = (csd[3] >> 1) & 0x7f;
+
+ fvdbg("CSD:\n");
+ fvdbg(" CSD_STRUCTURE: %d SPEC_VERS: %d (MMC)\n",
+ decoded.csdstructure, decoded.mmcspecvers);
+ fvdbg(" TAAC {TIME_UNIT: %d TIME_VALUE: %d} NSAC: %d\n",
+ decoded.taac.timeunit, decoded.taac.timevalue, decoded.nsac);
+ fvdbg(" TRAN_SPEED {TRANSFER_RATE_UNIT: %d TIME_VALUE: %d}\n",
+ decoded.transpeed.transferrateunit, decoded.transpeed.timevalue);
+ fvdbg(" CCC: %d\n", decoded.ccc);
+ fvdbg(" READ_BL_LEN: %d READ_BL_PARTIAL: %d\n",
+ decoded.readbllen, decoded.readblpartial);
+ fvdbg(" WRITE_BLK_MISALIGN: %d READ_BLK_MISALIGN: %d\n",
+ decoded.writeblkmisalign, decoded.readblkmisalign);
+ fvdbg(" DSR_IMP: %d\n",
+ decoded.dsrimp);
+
+ if (IS_BLOCK(priv->type))
+ {
+ fvdbg(" SD Block Addressing:\n");
+ fvdbg(" C_SIZE: %d SD_ER_BLK_EN: %d\n",
+ decoded.u.sdblock.csize, decoded.u.sdblock.sderblen);
+ fvdbg(" SD_SECTOR_SIZE: %d SD_WP_GRP_SIZE: %d\n",
+ decoded.u.sdblock.sdsectorsize, decoded.u.sdblock.sdwpgrpsize);
+ }
+ else if (IS_SD(priv->type))
+ {
+ fvdbg(" SD Byte Addressing:\n");
+ fvdbg(" C_SIZE: %d C_SIZE_MULT: %d\n",
+ decoded.u.sdbyte.csize, decoded.u.sdbyte.csizemult);
+ fvdbg(" VDD_R_CURR_MIN: %d VDD_R_CURR_MAX: %d\n",
+ decoded.u.sdbyte.vddrcurrmin, decoded.u.sdbyte.vddrcurrmax);
+ fvdbg(" VDD_W_CURR_MIN: %d VDD_W_CURR_MAX: %d\n",
+ decoded.u.sdbyte.vddwcurrmin, decoded.u.sdbyte.vddwcurrmax);
+ fvdbg(" SD_ER_BLK_EN: %d SD_SECTOR_SIZE: %d (SD) SD_WP_GRP_SIZE: %d\n",
+ decoded.u.sdbyte.sderblen, decoded.u.sdbyte.sdsectorsize, decoded.u.sdbyte.sdwpgrpsize);
+ }
+#ifdef CONFIG_MMCSD_MMCSUPPORT
+ else if (IS_MMC(priv->type))
+ {
+ fvdbg(" MMC:\n");
+ fvdbg(" C_SIZE: %d C_SIZE_MULT: %d\n",
+ decoded.u.mmc.csize, decoded.u.mmc.csizemult);
+ fvdbg(" VDD_R_CURR_MIN: %d VDD_R_CURR_MAX: %d\n",
+ decoded.u.mmc.vddrcurrmin, decoded.u.mmc.vddrcurrmax);
+ fvdbg(" VDD_W_CURR_MIN: %d VDD_W_CURR_MAX: %d\n",
+ decoded.u.mmc.vddwcurrmin, decoded.u.mmc.vddwcurrmax);
+ fvdbg(" MMC_SECTOR_SIZE: %d MMC_ER_GRP_SIZE: %d MMC_WP_GRP_SIZE: %d\n",
+ decoded.u.mmc.er.mmc22.sectorsize, decoded.u.mmc.er.mmc22.ergrpsize,
+ decoded.u.mmc.mmcwpgrpsize);
+ }
+#endif
+
+ fvdbg(" WP_GRP_EN: %d MMC DFLT_ECC: %d (MMC) R2W_FACTOR: %d\n",
+ decoded.wpgrpen, decoded.mmcdfltecc, decoded.r2wfactor);
+ fvdbg(" WRITE_BL_LEN: %d WRITE_BL_PARTIAL: %d\n",
+ decoded.writebllen, decoded.writeblpartial);
+ fvdbg(" FILE_FORMAT_GROUP: %d COPY: %d\n",
+ decoded.fileformatgrp, decoded.copy);
+ fvdbg(" PERM_WRITE_PROTECT: %d TMP_WRITE_PROTECT: %d\n",
+ decoded.permwriteprotect, decoded.tmpwriteprotect);
+ fvdbg(" FILE_FORMAT: %d ECC: %d (MMC) CRC: %d\n",
+ decoded.fileformat, decoded.mmcecc, decoded.crc);
+
+ fvdbg("Capacity: %luKb, Block size: %db, nblocks: %d wrprotect: %d\n",
+ (unsigned long)(priv->capacity / 1024), priv->blocksize,
+ priv->nblocks, priv->wrprotect);
+#endif
+}
+
+/****************************************************************************
+ * Name: mmcsd_decodeCID
+ *
+ * Description:
+ * Show the contents of the Card Indentification Data (CID) (for debug
+ * purposes only)
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+static void mmcsd_decodeCID(FAR struct mmcsd_state_s *priv, uint32_t cid[4])
+{
+ struct mmcsd_cid_s decoded;
+
+ /* Word 1: Bits 127-96:
+ * mid - 127-120 8-bit Manufacturer ID
+ * oid - 119-104 16-bit OEM/Application ID (ascii)
+ * pnm - 103-64 40-bit Product Name (ascii) + null terminator
+ * pnm[0] 103:96
+ */
+
+ decoded.mid = cid[0] >> 24;
+ decoded.oid = (cid[0] >> 16) & 0xffff;
+ decoded.pnm[0] = cid[0] & 0xff;
+
+ /* Word 2: Bits 64:95
+ * pnm - 103-64 40-bit Product Name (ascii) + null terminator
+ * pnm[1] 95:88
+ * pnm[2] 87:80
+ * pnm[3] 79:72
+ * pnm[4] 71:64
+ */
+
+ decoded.pnm[1] = cid[1] >> 24;
+ decoded.pnm[2] = (cid[1] >> 16) & 0xff;
+ decoded.pnm[3] = (cid[1] >> 8) & 0xff;
+ decoded.pnm[4] = cid[1] & 0xff;
+ decoded.pnm[5] = '\0';
+
+ /* Word 3: Bits 32-63
+ * prv - 63-56 8-bit Product revision
+ * psn - 55-24 32-bit Product serial number
+ */
+
+ decoded.prv = cid[2] >> 24;
+ decoded.psn = cid[2] << 8;
+
+ /* Word 4: Bits 0-31
+ * psn - 55-24 32-bit Product serial number
+ * 23-20 4-bit (reserved)
+ * mdt - 19:8 12-bit Manufacturing date
+ * crc - 7:1 7-bit CRC7
+ */
+
+ decoded.psn |= cid[3] >> 24;
+ decoded.mdt = (cid[3] >> 8) & 0x0fff;
+ decoded.crc = (cid[3] >> 1) & 0x7f;
+
+ fvdbg("mid: %02x oid: %04x pnm: %s prv: %d psn: %d mdt: %02x crc: %02x\n",
+ decoded.mid, decoded.oid, decoded.pnm, decoded.prv,
+ decoded.psn, decoded.mdt, decoded.crc);
+}
+#endif
+
+/****************************************************************************
+ * Name: mmcsd_decodeSCR
+ *
+ * Description:
+ * Show the contents of the SD Configuration Register (SCR). The only
+ * value retained is: priv->buswidth;
+ *
+ ****************************************************************************/
+
+static void mmcsd_decodeSCR(FAR struct mmcsd_state_s *priv, uint32_t scr[2])
+{
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+struct mmcsd_scr_s decoded;
+#endif
+
+ /* Word 1, bits 63:32
+ * SCR_STRUCTURE 63:60 4-bit SCR structure version
+ * SD_VERSION 59:56 4-bit SD memory spec. version
+ * DATA_STATE_AFTER_ERASE 55:55 1-bit erase status
+ * SD_SECURITY 54:52 3-bit SD security support level
+ * SD_BUS_WIDTHS 51:48 4-bit bus width indicator
+ * Reserved 47:32 16-bit SD reserved space
+ */
+
+#ifdef CONFIG_ENDIAN_BIG /* Card transfers SCR in big-endian order */
+ priv->buswidth = (scr[0] >> 16) & 15;
+#else
+ priv->buswidth = (scr[0] >> 8) & 15;
+#endif
+
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+#ifdef CONFIG_ENDIAN_BIG /* Card SCR is big-endian order / CPU also big-endian
+ * 60 56 52 48 44 40 36 32
+ * VVVV SSSS ESSS BBBB RRRR RRRR RRRR RRRR */
+ decoded.scrversion = scr[0] >> 28;
+ decoded.sdversion = (scr[0] >> 24) & 15;
+ decoded.erasestate = (scr[0] >> 23) & 1;
+ decoded.security = (scr[0] >> 20) & 7;
+#else /* Card SCR is big-endian order / CPU is little-endian
+ * 36 32 44 40 52 48 60 56
+ * RRRR RRRR RRRR RRRR ESSS BBBB VVVV SSSS */
+ decoded.scrversion = (scr[0] >> 4) & 15;
+ decoded.sdversion = scr[0] & 15;
+ decoded.erasestate = (scr[0] >> 15) & 1;
+ decoded.security = (scr[0] >> 12) & 7;
+#endif
+ decoded.buswidth = priv->buswidth;
+#endif
+
+ /* Word 1, bits 63:32
+ * Reserved 31:0 32-bits reserved for manufacturing usage.
+ */
+
+#if defined(CONFIG_DEBUG) && defined (CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
+ decoded.mfgdata = scr[1]; /* Might be byte reversed! */
+
+ fvdbg("SCR:\n");
+ fvdbg(" SCR_STRUCTURE: %d SD_VERSION: %d\n",
+ decoded.scrversion,decoded.sdversion);
+ fvdbg(" DATA_STATE_AFTER_ERASE: %d SD_SECURITY: %d SD_BUS_WIDTHS: %x\n",
+ decoded.erasestate, decoded.security, decoded.buswidth);
+ fvdbg(" Manufacturing data: %08x\n",
+ decoded.mfgdata);
+#endif
+}
+
+/****************************************************************************
+ * Name: mmcsd_getR1
+ *
+ * Description:
+ * Get the R1 status of the card using CMD13
+ *
+ ****************************************************************************/
+
+static int mmcsd_getR1(FAR struct mmcsd_state_s *priv, FAR uint32_t *r1)
+{
+ uint32_t localR1;
+ int ret;
+
+ DEBUGASSERT(priv != NULL && r1 != NULL);
+
+ /* Send CMD13, SEND_STATUS. The addressed card responds by sending its
+ * R1 card status register.
+ */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD13, (uint32_t)priv->rca << 16);
+ ret = SDIO_RECVR1(priv->dev, MMCSD_CMD13, &localR1);
+ if (ret == OK)
+ {
+ /* Check if R1 reports an error */
+
+ if ((localR1 & MMCSD_R1_ERRORMASK) != 0)
+ {
+ /* Card locked is considered an error. Save the card locked
+ * indication for later use.
+ */
+
+ priv->locked = ((localR1 & MMCSD_R1_CARDISLOCKED) != 0);
+ ret = -EIO;
+ }
+ else
+ {
+ /* No errors, return R1 */
+
+ *r1 = localR1;
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_verifystate
+ *
+ * Description:
+ * Verify that the card is in STANDBY state
+ *
+ ****************************************************************************/
+
+static int mmcsd_verifystate(FAR struct mmcsd_state_s *priv, uint32_t state)
+{
+ uint32_t r1;
+ int ret;
+
+ /* Get the current R1 status from the card */
+
+ ret = mmcsd_getR1(priv, &r1);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_getR1 failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Now check if the card is in the expected state. */
+
+ if (IS_STATE(r1, state))
+ {
+ /* Yes.. return Success */
+
+ priv->wrbusy = false;
+ return OK;
+ }
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Transfer Helpers
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mmcsd_wrprotected
+ *
+ * Description:
+ * Return true if the the card is unlocked an not write protected. The
+ *
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static bool mmcsd_wrprotected(FAR struct mmcsd_state_s *priv)
+{
+ /* Check if the card is locked (priv->locked) or write protected either (1)
+ * via software as reported via the CSD and retained in priv->wrprotect or
+ * (2) via the mechanical write protect on the card (which we get from the
+ * SDIO driver via SDIO_WRPROTECTED)
+ */
+
+ return (priv->wrprotect || priv->locked || SDIO_WRPROTECTED(priv->dev));
+}
+#endif
+
+/****************************************************************************
+ * Name: mmcsd_eventwait
+ *
+ * Description:
+ * Wait for the specified events to occur. Check for wakeup on error events.
+ *
+ ****************************************************************************/
+
+static int mmcsd_eventwait(FAR struct mmcsd_state_s *priv,
+ sdio_eventset_t failevents, uint32_t timeout)
+{
+ sdio_eventset_t wkupevent;
+
+ /* Wait for the set of events enabled by SDIO_EVENTENABLE. */
+
+ wkupevent = SDIO_EVENTWAIT(priv->dev, timeout);
+
+ /* SDIO_EVENTWAIT returns the event set containing the event(s) that ended
+ * the wait. It should always be non-zero, but may contain failure as
+ * well as success events. Check if it contains any failure events.
+ */
+
+ if ((wkupevent & failevents) != 0)
+ {
+ /* Yes.. the failure event is probably SDIOWAIT_TIMEOUT */
+
+ fdbg("ERROR: Awakened with %02x\n", wkupevent);
+ return wkupevent & SDIOWAIT_TIMEOUT ? -ETIMEDOUT : -EIO;
+ }
+
+ /* Since there are no failure events, we must have been awakened by one
+ * (or more) success events.
+ */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mmcsd_transferready
+ *
+ * Description:
+ * Check if the MMC/SD card is ready for the next read or write transfer.
+ * Ready means: (1) card still in the slot, and (2) if the last transfer
+ * was a write transfer, the card is no longer busy from that transfer.
+ *
+ ****************************************************************************/
+
+static int mmcsd_transferready(FAR struct mmcsd_state_s *priv)
+{
+ uint32_t starttime;
+ uint32_t elapsed;
+ uint32_t r1;
+ int ret;
+
+ /* First, check if the card has been removed. */
+
+ if (!SDIO_PRESENT(priv->dev))
+ {
+ fdbg("ERROR: Card has been removed\n");
+ return -ENODEV;
+ }
+
+ /* If the last data transfer was not a write, then we do not have to check
+ * the card status.
+ */
+
+ else if (!priv->wrbusy)
+ {
+ return OK;
+ }
+
+ /* The card is still present and the last transfer was a write transfer.
+ * Loop, querying the card state. Return when (1) the card is in the TRANSFER
+ * state, (2) the card stays in the PROGRAMMING state too long, or (3) the
+ * card is in any other state.
+ *
+ * The PROGRAMMING state occurs normally after a WRITE operation. During this
+ * time, the card may be busy completing the WRITE and is not available for
+ * other operations. The card will transition from the PROGRAMMING state to
+ * the TRANSFER state when the card completes the WRITE operation.
+ */
+
+ starttime = clock_systimer();
+ do
+ {
+ /* Get the current R1 status from the card */
+
+ ret = mmcsd_getR1(priv, &r1);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_getR1 failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Now check if the card is in the expected transfer state. */
+
+ if (IS_STATE(r1, MMCSD_R1_STATE_TRAN))
+ {
+ /* Yes.. return Success */
+
+ priv->wrbusy = false;
+ return OK;
+ }
+
+ /* Check for the programming state. This is not an error. It means
+ * that the card is still busy from the last (write) transfer.
+ */
+
+ else if (!IS_STATE(r1, MMCSD_R1_STATE_PRG))
+ {
+ /* Any other state would be an error in this context. There is
+ * a possibility that the card is not selected. In this case,
+ * it could be in STANDBY or DISCONNECTED state and the fix
+ * might b to send CMD7 to re-select the card. Consider this
+ * if this error occurs.
+ */
+
+ fdbg("ERROR: Unexpected R1 state: %08x\n", r1);
+ return -EINVAL;
+ }
+
+ /* We are still in the programming state. Calculate the elapsed
+ * time... we can't stay in this loop forever!
+ */
+
+ elapsed = clock_systimer() - starttime;
+ }
+ while (elapsed < TICK_PER_SEC);
+ return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Name: mmcsd_stoptransmission
+ *
+ * Description:
+ * Send STOP_TRANSMISSION
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MMCSD_MULTIBLOCK_DISABLE
+static int mmcsd_stoptransmission(FAR struct mmcsd_state_s *priv)
+{
+ int ret;
+
+ /* Send CMD12, STOP_TRANSMISSION, and verify good R1 return status */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD12, 0);
+ ret = mmcsd_recvR1(priv, MMCSD_CMD12);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_recvR1 for CMD12 failed: %d\n", ret);
+ }
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: mmcsd_setblocklen
+ *
+ * Description:
+ * Read a single block of data.
+ *
+ ****************************************************************************/
+
+static int mmcsd_setblocklen(FAR struct mmcsd_state_s *priv, uint32_t blocklen)
+{
+ int ret = OK;
+
+ /* Is the block length already selected in the card? */
+
+ if (priv->selblocklen != blocklen)
+ {
+ /* Send CMD16 = SET_BLOCKLEN. This command sets the block length (in
+ * bytes) for all following block commands (read and write). Default
+ * block length is specified in the CSD.
+ */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD16, blocklen);
+ ret = mmcsd_recvR1(priv, MMCSD_CMD16);
+ if (ret == OK)
+ {
+ priv->selblocklen = blocklen;
+ }
+ else
+ {
+ fdbg("ERROR: mmcsd_recvR1 for CMD16 failed: %d\n", ret);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_readsingle
+ *
+ * Description:
+ * Read a single block of data.
+ *
+ ****************************************************************************/
+
+static ssize_t mmcsd_readsingle(FAR struct mmcsd_state_s *priv,
+ FAR uint8_t *buffer, off_t startblock)
+{
+ off_t offset;
+ int ret;
+
+ fvdbg("startblock=%d\n", startblock);
+ DEBUGASSERT(priv != NULL && buffer != NULL);
+
+ /* Check if the card is locked */
+
+ if (priv->locked)
+ {
+ fdbg("ERROR: Card is locked\n");
+ return -EPERM;
+ }
+
+ /* Verify that the card is ready for the transfer. The card may still be
+ * busy from the preceding write transfer. It would be simpler to check
+ * for write busy at the end of each write, rather than at the beginning of
+ * each read AND write, but putting the busy-wait at the beginning of the
+ * transfer allows for more overlap and, hopefully, better performance
+ */
+
+ ret = mmcsd_transferready(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Card not ready: %d\n", ret);
+ return ret;
+ }
+
+ /* If this is a byte addressed SD card, then convert sector start sector
+ * number to a byte offset
+ */
+
+ if (IS_BLOCK(priv->type))
+ {
+ offset = startblock;
+ }
+ else
+ {
+ offset = startblock << priv->blockshift;
+ }
+ fvdbg("offset=%d\n", offset);
+
+ /* Select the block size for the card */
+
+ ret = mmcsd_setblocklen(priv, priv->blocksize);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_setblocklen failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Configure SDIO controller hardware for the read transfer */
+
+ SDIO_BLOCKSETUP(priv->dev, priv->blocksize, 1);
+ SDIO_WAITENABLE(priv->dev, SDIOWAIT_TRANSFERDONE|SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR);
+#ifdef CONFIG_SDIO_DMA
+ if (priv->dma)
+ {
+ SDIO_DMARECVSETUP(priv->dev, buffer, priv->blocksize);
+ }
+ else
+#endif
+ {
+ SDIO_RECVSETUP(priv->dev, buffer, priv->blocksize);
+ }
+
+ /* Send CMD17, READ_SINGLE_BLOCK: Read a block of the size selected
+ * by the mmcsd_setblocklen() and verify that good R1 status is
+ * returned. The card state should change from Transfer to Sending-Data
+ * state.
+ */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD17, offset);
+ ret = mmcsd_recvR1(priv, MMCSD_CMD17);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_recvR1 for CMD17 failed: %d\n", ret);
+ SDIO_CANCEL(priv->dev);
+ return ret;
+ }
+
+ /* Then wait for the data transfer to complete */
+
+ ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR, MMCSD_BLOCK_DATADELAY);
+ if (ret != OK)
+ {
+ fdbg("ERROR: CMD17 transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Return value: One sector read */
+
+ return 1;
+}
+
+/****************************************************************************
+ * Name: mmcsd_readmultiple
+ *
+ * Description:
+ * Read multiple, contiguous blocks of data from the physical device.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MMCSD_MULTIBLOCK_DISABLE
+static ssize_t mmcsd_readmultiple(FAR struct mmcsd_state_s *priv,
+ FAR uint8_t *buffer, off_t startblock,
+ size_t nblocks)
+{
+ size_t nbytes;
+ off_t offset;
+ int ret;
+
+ fvdbg("startblock=%d nblocks=%d\n", startblock, nblocks);
+ DEBUGASSERT(priv != NULL && buffer != NULL && nblocks > 1);
+
+ /* Check if the card is locked */
+
+ if (priv->locked)
+ {
+ fdbg("ERROR: Card is locked\n");
+ return -EPERM;
+ }
+
+ /* Verify that the card is ready for the transfer. The card may still be
+ * busy from the preceding write transfer. It would be simpler to check
+ * for write busy at the end of each write, rather than at the beginning of
+ * each read AND write, but putting the busy-wait at the beginning of the
+ * transfer allows for more overlap and, hopefully, better performance
+ */
+
+ ret = mmcsd_transferready(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Card not ready: %d\n", ret);
+ return ret;
+ }
+
+ /* If this is a byte addressed SD card, then convert both the total transfer
+ * size to bytes and the sector start sector number to a byte offset
+ */
+
+ nbytes = nblocks << priv->blockshift;
+ if (IS_BLOCK(priv->type))
+ {
+ offset = startblock;
+ }
+ else
+ {
+ offset = startblock << priv->blockshift;
+ }
+ fvdbg("nbytes=%d byte offset=%d\n", nbytes, offset);
+
+ /* Select the block size for the card */
+
+ ret = mmcsd_setblocklen(priv, priv->blocksize);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_setblocklen failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Configure SDIO controller hardware for the read transfer */
+
+ SDIO_BLOCKSETUP(priv->dev, priv->blocksize, nblocks);
+ SDIO_WAITENABLE(priv->dev, SDIOWAIT_TRANSFERDONE|SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR);
+#ifdef CONFIG_SDIO_DMA
+ if (priv->dma)
+ {
+ SDIO_DMARECVSETUP(priv->dev, buffer, nbytes);
+ }
+ else
+#endif
+ {
+ SDIO_RECVSETUP(priv->dev, buffer, nbytes);
+ }
+
+ /* Send CMD18, READ_MULT_BLOCK: Read a block of the size selected by
+ * the mmcsd_setblocklen() and verify that good R1 status is returned
+ */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD18, offset);
+ ret = mmcsd_recvR1(priv, MMCSD_CMD18);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_recvR1 for CMD18 failed: %d\n", ret);
+ SDIO_CANCEL(priv->dev);
+ return ret;
+ }
+
+ /* Wait for the transfer to complete */
+
+ ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR, nblocks * MMCSD_BLOCK_DATADELAY);
+ if (ret != OK)
+ {
+ fdbg("ERROR: CMD18 transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Send STOP_TRANSMISSION */
+
+ ret = mmcsd_stoptransmission(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_stoptransmission failed: %d\n", ret);
+ }
+
+ /* On success, return the number of blocks read */
+
+ return nblocks;
+}
+#endif
+
+/****************************************************************************
+ * Name: mmcsd_reload
+ *
+ * Description:
+ * Reload the specified number of sectors from the physical device into the
+ * read-ahead buffer.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_READAHEAD
+static ssize_t mmcsd_reload(FAR void *dev, FAR uint8_t *buffer,
+ off_t startblock, size_t nblocks)
+{
+ FAR struct mmcsd_state_s *priv = (FAR struct mmcsd_state_s *)dev;
+#ifdef CONFIG_MMCSD_MULTIBLOCK_DISABLE
+ size_t block;
+ size_t endblock;
+#endif
+ ssize_t ret;
+
+ DEBUGASSERT(priv != NULL && buffer != NULL && nblocks > 0)
+
+#ifdef CONFIG_MMCSD_MULTIBLOCK_DISABLE
+ /* Read each block using only the single block transfer method */
+
+ endblock = startblock + nblocks - 1;
+ for (block = startblock; block <= endblock; block++)
+ {
+ /* Read this block into the user buffer */
+
+ ret = mmcsd_readsingle(priv, buffer, block);
+ if (ret < 0)
+ {
+ break;
+ }
+
+ /* Increment the buffer pointer by the block size */
+
+ buffer += priv->blocksize;
+ }
+#else
+ /* Use either the single- or muliple-block transfer method */
+
+ if (nblocks == 1)
+ {
+ ret = mmcsd_readsingle(priv, buffer, startblock);
+ }
+ else
+ {
+ ret = mmcsd_readmultiple(priv, buffer, startblock, nblocks);
+ }
+#endif
+
+ /* On success, return the number of blocks read */
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: mmcsd_writesingle
+ *
+ * Description:
+ * Write a single block of data to the physical device.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t mmcsd_writesingle(FAR struct mmcsd_state_s *priv,
+ FAR const uint8_t *buffer, off_t startblock)
+{
+ off_t offset;
+ int ret;
+
+ fvdbg("startblock=%d\n", startblock);
+ DEBUGASSERT(priv != NULL && buffer != NULL);
+
+ /* Check if the card is locked or write protected (either via software or
+ * via the mechanical write protect on the card)
+ */
+
+ if (mmcsd_wrprotected(priv))
+ {
+ fdbg("ERROR: Card is locked or write protected\n");
+ return -EPERM;
+ }
+
+ /* Verify that the card is ready for the transfer. The card may still be
+ * busy from the preceding write transfer. It would be simpler to check
+ * for write busy at the end of each write, rather than at the beginning of
+ * each read AND write, but putting the busy-wait at the beginning of the
+ * transfer allows for more overlap and, hopefully, better performance
+ */
+
+ ret = mmcsd_transferready(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Card not ready: %d\n", ret);
+ return ret;
+ }
+
+ /* If this is a byte addressed SD card, then convert sector start sector
+ * number to a byte offset
+ */
+
+ if (IS_BLOCK(priv->type))
+ {
+ offset = startblock;
+ }
+ else
+ {
+ offset = startblock << priv->blockshift;
+ }
+ fvdbg("offset=%d\n", offset);
+
+ /* Select the block size for the card */
+
+ ret = mmcsd_setblocklen(priv, priv->blocksize);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_setblocklen failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Send CMD24, WRITE_BLOCK, and verify that good R1 status is returned */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD24, offset);
+ ret = mmcsd_recvR1(priv, MMCSD_CMD24);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_recvR1 for CMD24 failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Configure SDIO controller hardware for the write transfer */
+
+ SDIO_BLOCKSETUP(priv->dev, priv->blocksize, 1);
+ SDIO_WAITENABLE(priv->dev, SDIOWAIT_TRANSFERDONE|SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR);
+#ifdef CONFIG_SDIO_DMA
+ if (priv->dma)
+ {
+ SDIO_DMASENDSETUP(priv->dev, buffer, priv->blocksize);
+ }
+ else
+#endif
+ {
+ SDIO_SENDSETUP(priv->dev, buffer, priv->blocksize);
+ }
+
+ /* Flag that a write transfer is pending that we will have to check for
+ * write complete at the beginning of the next transfer.
+ */
+
+ priv->wrbusy = true;
+
+ /* Wait for the transfer to complete */
+
+ ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR, MMCSD_BLOCK_DATADELAY);
+ if (ret != OK)
+ {
+ fdbg("ERROR: CMD24 transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ /* On success, return the number of blocks written */
+
+ return 1;
+}
+#endif
+
+/****************************************************************************
+ * Name: mmcsd_writemultiple
+ *
+ * Description:
+ * Write multiple, contiguous blocks of data to the physical device.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_FS_WRITABLE) && !defined(CONFIG_MMCSD_MULTIBLOCK_DISABLE)
+static ssize_t mmcsd_writemultiple(FAR struct mmcsd_state_s *priv,
+ FAR const uint8_t *buffer, off_t startblock,
+ size_t nblocks)
+{
+ off_t offset;
+ size_t nbytes;
+ int ret;
+
+ fvdbg("startblockr=%d nblocks=%d\n", startblock, nblocks);
+ DEBUGASSERT(priv != NULL && buffer != NULL && nblocks > 1);
+
+ /* Check if the card is locked or write protected (either via software or
+ * via the mechanical write protect on the card)
+ */
+
+ if (mmcsd_wrprotected(priv))
+ {
+ fdbg("ERROR: Card is locked or write protected\n");
+ return -EPERM;
+ }
+
+ /* Verify that the card is ready for the transfer. The card may still be
+ * busy from the preceding write transfer. It would be simpler to check
+ * for write busy at the end of each write, rather than at the beginning of
+ * each read AND write, but putting the busy-wait at the beginning of the
+ * transfer allows for more overlap and, hopefully, better performance
+ */
+
+ ret = mmcsd_transferready(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Card not ready: %d\n", ret);
+ return ret;
+ }
+
+ /* If this is a byte addressed SD card, then convert both the total transfer
+ * size to bytes and the sector start sector number to a byte offset
+ */
+
+ nbytes = nblocks << priv->blockshift;
+ if (IS_BLOCK(priv->type))
+ {
+ offset = startblock;
+ }
+ else
+ {
+ offset = startblock << priv->blockshift;
+ }
+ fvdbg("nbytes=%d byte offset=%d\n", nbytes, offset);
+
+ /* Select the block size for the card */
+
+ ret = mmcsd_setblocklen(priv, priv->blocksize);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_setblocklen failed: %d\n", ret);
+ return ret;
+ }
+
+ /* If this is an SD card, then send ACMD23 (SET_WR_BLK_COUNT) just before
+ * sending CMD25 (WRITE_MULTIPLE_BLOCK). This sets the number of write
+ * blocks to be pre-erased and might make the following multiple block write
+ * command faster.
+ */
+
+ if (IS_SD(priv->type))
+ {
+ /* Send CMD55, APP_CMD, a verify that good R1 status is retured */
+
+ mmcsd_sendcmdpoll(priv, SD_CMD55, 0);
+ ret = mmcsd_recvR1(priv, SD_CMD55);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_recvR1 for CMD55 (ACMD23) failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Send CMD23, SET_WR_BLK_COUNT, and verify that good R1 status is returned */
+
+ mmcsd_sendcmdpoll(priv, SD_ACMD23, 0);
+ ret = mmcsd_recvR1(priv, SD_ACMD23);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_recvR1 for ACMD23 failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* Send CMD25, WRITE_MULTIPLE_BLOCK, and verify that good R1 status
+ * is returned
+ */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD25, offset);
+ ret = mmcsd_recvR1(priv, MMCSD_CMD25);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_recvR1 for CMD24 failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Configure SDIO controller hardware for the write transfer */
+
+ SDIO_BLOCKSETUP(priv->dev, priv->blocksize, nblocks);
+ SDIO_WAITENABLE(priv->dev, SDIOWAIT_TRANSFERDONE|SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR);
+#ifdef CONFIG_SDIO_DMA
+ if (priv->dma)
+ {
+ SDIO_DMASENDSETUP(priv->dev, buffer, nbytes);
+ }
+ else
+#endif
+ {
+ SDIO_SENDSETUP(priv->dev, buffer, nbytes);
+ }
+
+ /* Flag that a write transfer is pending that we will have to check for
+ * write complete at the beginning of the next transfer.
+ */
+
+ priv->wrbusy = true;
+
+ /* Wait for the transfer to complete */
+
+ ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR, nblocks * MMCSD_BLOCK_DATADELAY);
+ if (ret != OK)
+ {
+ fdbg("ERROR: CMD18 transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Send STOP_TRANSMISSION */
+
+ ret = mmcsd_stoptransmission(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_stoptransmission failed: %d\n", ret);
+ return ret;
+ }
+
+ /* On success, return the number of blocks read */
+
+ return nblocks;
+}
+#endif
+
+/****************************************************************************
+ * Name: mmcsd_flush
+ *
+ * Description:
+ * Flush the specified number of sectors from the write buffer to the card.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER)
+static ssize_t mmcsd_flush(FAR void *dev, FAR const uint8_t *buffer,
+ off_t startblock, size_t nblocks)
+{
+ FAR struct mmcsd_state_s *priv = (FAR struct mmcsd_state_s *)dev;
+#ifndef CONFIG_MMCSD_MULTIBLOCK_DISABLE
+ size_t block;
+ size_t endblock;
+#endif
+ ssize_t ret;
+
+ DEBUGASSERT(priv != NULL && buffer != NULL && nblocks > 0)
+
+#ifdef CONFIG_MMCSD_MULTIBLOCK_DISABLE
+ /* Write each block using only the single block transfer method */
+
+ endblock = startblock + nblocks - 1;
+ for (block = startblock; block <= endblock; block++)
+ {
+ /* Write this block from the user buffer */
+
+ ret = mmcsd_writesingle(priv, buffer, block);
+ if (ret < 0)
+ {
+ break;
+ }
+
+ /* Increment the buffer pointer by the block size */
+
+ buffer += priv->blocksize;
+ }
+#else
+ if (nblocks == 1)
+ {
+ ret = mmcsd_writesingle(priv, buffer, startblock);
+ }
+ else
+ {
+ ret = mmcsd_writemultiple(priv, buffer, startblock, nblocks);
+ }
+#endif
+
+ /* On success, return the number of blocks written */
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Block Driver Methods
+ ****************************************************************************/
+/****************************************************************************
+ * Name: mmcsd_open
+ *
+ * Description: Open the block device
+ *
+ ****************************************************************************/
+
+static int mmcsd_open(FAR struct inode *inode)
+{
+ FAR struct mmcsd_state_s *priv;
+
+ fvdbg("Entry\n");
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct mmcsd_state_s *)inode->i_private;
+
+ /* Just increment the reference count on the driver */
+
+ DEBUGASSERT(priv->crefs < MAX_CREFS);
+ mmcsd_takesem(priv);
+ priv->crefs++;
+ mmcsd_givesem(priv);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mmcsd_close
+ *
+ * Description: close the block device
+ *
+ ****************************************************************************/
+
+static int mmcsd_close(FAR struct inode *inode)
+{
+ FAR struct mmcsd_state_s *priv;
+
+ fvdbg("Entry\n");
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct mmcsd_state_s *)inode->i_private;
+
+ /* Decrement the reference count on the block driver */
+
+ DEBUGASSERT(priv->crefs > 0);
+ mmcsd_takesem(priv);
+ priv->crefs--;
+ mmcsd_givesem(priv);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mmcsd_read
+ *
+ * Description:
+ * Read the specified numer of sectors from the read-ahead buffer or from
+ * the physical device.
+ *
+ ****************************************************************************/
+
+static ssize_t mmcsd_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t startsector, unsigned int nsectors)
+{
+ FAR struct mmcsd_state_s *priv;
+#if !defined(CONFIG_FS_READAHEAD) && defined(CONFIG_MMCSD_MULTIBLOCK_DISABLE)
+ size_t sector;
+ size_t endsector;
+#endif
+ ssize_t ret = 0;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct mmcsd_state_s *)inode->i_private;
+ fvdbg("startsector: %d nsectors: %d sectorsize: %d\n",
+ startsector, nsectors, priv->blocksize);
+
+ if (nsectors > 0)
+ {
+ mmcsd_takesem(priv);
+
+#if defined(CONFIG_FS_READAHEAD)
+ /* Get the data from the read-ahead buffer */
+
+ ret = rwb_read(&priv->rwbuffer, startsector, nsectors, buffer);
+
+#elif defined(CONFIG_MMCSD_MULTIBLOCK_DISABLE)
+ /* Read each block using only the single block transfer method */
+
+ endsector = startsector + nsectors - 1;
+ for (sector = startsector; sector <= endsector; sector++)
+ {
+ /* Read this sector into the user buffer */
+
+ ret = mmcsd_readsingle(priv, buffer, sector);
+ if (ret < 0)
+ {
+ break;
+ }
+
+ /* Increment the buffer pointer by the sector size */
+
+ buffer += priv->blocksize;
+ }
+#else
+ /* Use either the single- or muliple-block transfer method */
+
+ if (nsectors == 1)
+ {
+ ret = mmcsd_readsingle(priv, buffer, startsector);
+ }
+ else
+ {
+ ret = mmcsd_readmultiple(priv, buffer, startsector, nsectors);
+ }
+#endif
+ mmcsd_givesem(priv);
+ }
+
+ /* On success, return the number of blocks read */
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_write
+ *
+ * Description:
+ * Write the specified number of sectors to the write buffer or to the
+ * physical device.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t mmcsd_write(FAR struct inode *inode, FAR const unsigned char *buffer,
+ size_t startsector, unsigned int nsectors)
+{
+ FAR struct mmcsd_state_s *priv;
+#if !defined(CONFIG_FS_WRITEBUFFER) && defined(CONFIG_MMCSD_MULTIBLOCK_DISABLE)
+ size_t sector;
+ size_t endsector;
+#endif
+ ssize_t ret = 0;
+
+ fvdbg("sector: %d nsectors: %d sectorsize: %d\n");
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct mmcsd_state_s *)inode->i_private;
+
+ mmcsd_takesem(priv);
+
+#if defined(CONFIG_FS_WRITEBUFFER)
+ /* Write the data to the write buffer */
+
+ ret = rwb_write(&priv->rwbuffer, startsector, nsectors, buffer);
+
+#elif defined(CONFIG_MMCSD_MULTIBLOCK_DISABLE)
+ /* Write each block using only the single block transfer method */
+
+ endsector = startsector + nsectors - 1;
+ for (sector = startsector; sector <= endsector; sector++)
+ {
+ /* Write this block from the user buffer */
+
+ ret = mmcsd_writesingle(priv, buffer, sector);
+ if (ret < 0)
+ {
+ break;
+ }
+
+ /* Increment the buffer pointer by the block size */
+
+ buffer += priv->blocksize;
+ }
+#else
+ /* Use either the single- or multiple-block transfer method */
+
+ if (nsectors == 1)
+ {
+ ret = mmcsd_writesingle(priv, buffer, startsector);
+ }
+ else
+ {
+ ret = mmcsd_writemultiple(priv, buffer, startsector, nsectors);
+ }
+#endif
+ mmcsd_givesem(priv);
+
+ /* On success, return the number of blocks written */
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: mmcsd_geometry
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int mmcsd_geometry(FAR struct inode *inode, struct geometry *geometry)
+{
+ FAR struct mmcsd_state_s *priv;
+ int ret = -EINVAL;
+
+ fvdbg("Entry\n");
+ DEBUGASSERT(inode && inode->i_private);
+
+ if (geometry)
+ {
+ /* Is there a (supported) card inserted in the slot? */
+
+ priv = (FAR struct mmcsd_state_s *)inode->i_private;
+ mmcsd_takesem(priv);
+ if (IS_EMPTY(priv))
+ {
+ /* No.. return ENODEV */
+
+ fvdbg("IS_EMPTY\n");
+ ret = -ENODEV;
+ }
+ else
+ {
+ /* Yes.. return the geometry of the card */
+
+ geometry->geo_available = true;
+ geometry->geo_mediachanged = priv->mediachanged;
+#ifdef CONFIG_FS_WRITABLE
+ geometry->geo_writeenabled = !mmcsd_wrprotected(priv);
+#else
+ geometry->geo_writeenabled = false;
+#endif
+ geometry->geo_nsectors = priv->nblocks;
+ geometry->geo_sectorsize = priv->blocksize;
+
+ fvdbg("available: true mediachanged: %s writeenabled: %s\n",
+ geometry->geo_mediachanged ? "true" : "false",
+ geometry->geo_writeenabled ? "true" : "false");
+ fvdbg("nsectors: %ld sectorsize: %d\n",
+ (long)geometry->geo_nsectors, geometry->geo_sectorsize);
+
+ priv->mediachanged = false;
+ ret = OK;
+ }
+ mmcsd_givesem(priv);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_ioctl
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int mmcsd_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
+{
+ FAR struct mmcsd_state_s *priv;
+ int ret;
+
+ fvdbg("Entry\n");
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct mmcsd_state_s *)inode->i_private;
+
+ /* Process the IOCTL by command */
+
+ mmcsd_takesem(priv);
+ switch (cmd)
+ {
+ case BIOC_PROBE: /* Check for media in the slot */
+ {
+ fvdbg("BIOC_PROBE\n");
+
+ /* Probe the MMC/SD slot for media */
+
+ ret = mmcsd_probe(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_probe failed: %d\n", ret);
+ }
+ }
+ break;
+
+ case BIOC_EJECT: /* Media has been removed from the slot */
+ {
+ fvdbg("BIOC_EJECT\n");
+
+ /* Process the removal of the card */
+
+ ret = mmcsd_removed(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_removed failed: %d\n", ret);
+ }
+
+ /* Enable logic to detect if a card is re-inserted */
+
+ SDIO_CALLBACKENABLE(priv->dev, SDIOMEDIA_INSERTED);
+ }
+ break;
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ mmcsd_givesem(priv);
+ return ret;
+}
+
+/****************************************************************************
+ * Initialization/uninitialization/reset
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mmcsd_mediachange
+ *
+ * Description:
+ * This is a callback function from the SDIO driver that indicates that
+ * there has been a change in the slot... either a card has been inserted
+ * or a card has been removed.
+ *
+ * Assumptions:
+ * This callback is NOT supposd to run in the context of an interrupt
+ * handler; it is probably running in the context of work thread.
+ *
+ ****************************************************************************/
+
+static void mmcsd_mediachange(FAR void *arg)
+{
+ FAR struct mmcsd_state_s *priv = (FAR struct mmcsd_state_s *)arg;
+
+ fvdbg("arg: %p\n", arg);
+ DEBUGASSERT(priv);
+
+ /* Is there a card present in the slot? */
+
+ mmcsd_takesem(priv);
+ if (SDIO_PRESENT(priv->dev))
+ {
+ /* No... process the card insertion. This could cause chaos if we think
+ * that a card is already present and there are mounted filesystems!
+ * NOTE that mmcsd_probe() will always re-enable callbacks appropriately.
+ */
+
+ (void)mmcsd_probe(priv);
+ }
+ else
+ {
+ /* No... process the card removal. This could have very bad implications
+ * for any mounted file systems! NOTE that mmcsd_removed() does NOT
+ * re-enable callbacks so we will need to do that here.
+ */
+
+ (void)mmcsd_removed(priv);
+
+ /* Enable logic to detect if a card is re-inserted */
+
+ SDIO_CALLBACKENABLE(priv->dev, SDIOMEDIA_INSERTED);
+ }
+ mmcsd_givesem(priv);
+}
+
+/****************************************************************************
+ * Name: mmcsd_widebus
+ *
+ * Description:
+ * An SD card has been inserted and its SCR has been obtained. Select wide
+ * (4-bit) bus operation if the card supports it.
+ *
+ * Assumptions:
+ * This function is called only once per card insertion as part of the SD
+ * card initialization sequence. It is not necessary to reselect the card
+ * there is not need to check if wide bus operation has already been
+ * selected.
+ *
+ ****************************************************************************/
+
+static int mmcsd_widebus(FAR struct mmcsd_state_s *priv)
+{
+#ifndef CONFIG_SDIO_WIDTH_D1_ONLY
+ int ret;
+
+ /* Check if the SD card supports this feature (as reported in the SCR) */
+
+ if ((priv->buswidth & MMCSD_SCR_BUSWIDTH_4BIT) != 0)
+ {
+ /* Disconnect any CD/DAT3 pull up using ACMD42. ACMD42 is optional and
+ * need not be supported by all SD calls.
+ *
+ * First end CMD55 APP_CMD with argument as card's RCA.
+ */
+
+ mmcsd_sendcmdpoll(priv, SD_CMD55, (uint32_t)priv->rca << 16);
+ ret = mmcsd_recvR1(priv, SD_CMD55);
+ if (ret != OK)
+ {
+ fdbg("ERROR: RECVR1 for CMD55 of ACMD42: %d\n", ret);
+ return ret;
+ }
+
+ /* Then send ACMD42 with the argument to disconnect the CD/DAT3
+ * pullup
+ */
+
+ mmcsd_sendcmdpoll(priv, SD_ACMD42, MMCSD_ACMD42_CD_DISCONNECT);
+ ret = mmcsd_recvR1(priv, SD_ACMD42);
+ if (ret != OK)
+ {
+ fvdbg("WARNING: SD card does not support ACMD42: %d\n", ret);
+ return ret;
+ }
+
+ /* Now send ACMD6 to select wide, 4-bit bus operation, beginning
+ * with CMD55, APP_CMD:
+ */
+
+ mmcsd_sendcmdpoll(priv, SD_CMD55, (uint32_t)priv->rca << 16);
+ ret = mmcsd_recvR1(priv, SD_CMD55);
+ if (ret != OK)
+ {
+ fdbg("ERROR: RECVR1 for CMD55 of ACMD6: %d\n", ret);
+ return ret;
+ }
+
+ /* Then send ACMD6 */
+
+ mmcsd_sendcmdpoll(priv, SD_ACMD6, MMCSD_ACMD6_BUSWIDTH_4);
+ ret = mmcsd_recvR1(priv, SD_ACMD6);
+ if (ret != OK)
+ {
+ return ret;
+ }
+
+ /* Configure the SDIO peripheral */
+
+ fvdbg("Wide bus operation selected\n");
+ SDIO_WIDEBUS(priv->dev, true);
+ priv->widebus = true;
+
+ SDIO_CLOCK(priv->dev, CLOCK_SD_TRANSFER_4BIT);
+ up_udelay(MMCSD_CLK_DELAY);
+ return OK;
+ }
+
+ /* Wide bus operation not supported */
+
+ fdbg("WARNING: Card does not support wide-bus operation\n");
+ return -ENOSYS;
+
+#else /* CONFIG_SDIO_WIDTH_D1_ONLY */
+
+ fvdbg("Wide-bus operation is disabled\n");
+ return -ENOSYS;
+
+#endif /* CONFIG_SDIO_WIDTH_D1_ONLY */
+}
+
+/****************************************************************************
+ * Name: mmcsd_mmcinitialize
+ *
+ * Description:
+ * We believe that there is an MMC card in the slot. Attempt to initialize
+ * and configure the MMC card. This is called only from mmcsd_probe().
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MMCSD_MMCSUPPORT
+static int mmcsd_mmcinitialize(FAR struct mmcsd_state_s *priv)
+{
+ uint32_t cid[4];
+ uint32_t csd[4];
+ int ret;
+
+ /* At this point, slow, ID mode clocking has been supplied to the card
+ * and CMD0 has been sent successfully. CMD1 succeeded and ACMD41 failed
+ * so there is good evidence that we have an MMC card inserted into the
+ * slot.
+ *
+ * Send CMD2, ALL_SEND_CID. This implementation supports only one MMC slot.
+ * If mulitple cards were installed, each card would respond to CMD2 by
+ * sending its CID (only one card completes the response at a time). The
+ * driver should send CMD2 and assign an RCAs until no response to
+ * ALL_SEND_CID is received. CMD2 causes transition to identification state/
+ * card-identification mode */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD2, 0);
+ ret = SDIO_RECVR2(priv->dev, MMCSD_CMD2, cid);
+ if (ret != OK)
+ {
+ fdbg("ERROR: SDIO_RECVR2 for MMC CID failed: %d\n", ret);
+ return ret;
+ }
+ mmcsd_decodeCID(priv, cid);
+
+ /* Send CMD3, SET_RELATIVE_ADDR. This command is used to assign a logical
+ * address to the card. For MMC, the host assigns the address. CMD3 causes
+ * transition to standby state/data-transfer mode
+ */
+
+ priv->rca = 1; /* There is only one card */
+ mmcsd_sendcmdpoll(priv, MMC_CMD3, priv->rca << 16);
+ ret = mmcsd_recvR1(priv, MMC_CMD3);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_recvR1(CMD3) failed: %d\n", ret);
+ return ret;
+ }
+
+ /* This should have caused a transition to standby state. However, this will
+ * not be reflected in the present R1 status. R1/6 contains the state of the
+ * card when the command was received, not when it completed execution.
+ *
+ * Verify that we are in standby state/data-transfer mode
+ */
+
+ ret = mmcsd_verifystate(priv, MMCSD_R1_STATE_STBY);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Failed to enter standby state\n");
+ return ret;
+ }
+
+ /* Send CMD9, SEND_CSD in standby state/data-transfer mode to obtain the
+ * Card Specific Data (CSD) register, e.g., block length, card storage
+ * capacity, etc. (Stays in standy state/data-transfer mode)
+ */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD9, priv->rca << 16);
+ ret = SDIO_RECVR2(priv->dev, MMCSD_CMD9, csd);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Could not get SD CSD register: %d\n", ret);
+ return ret;
+ }
+ mmcsd_decodeCSD(priv, csd);
+
+ /* Set the Driver Stage Register (DSR) if (1) a CONFIG_MMCSD_DSR has been
+ * provided and (2) the card supports a DSR register. If no DSR value
+ * the card default value (0x0404) will be used.
+ */
+
+ (void)mmcsd_sendcmd4(priv);
+
+ /* Select high speed MMC clocking (which may depend on the DSR setting) */
+
+ SDIO_CLOCK(priv->dev, CLOCK_MMC_TRANSFER);
+ up_udelay(MMCSD_CLK_DELAY);
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: mmcsd_sdinitialize
+ *
+ * Description:
+ * We believe that there is an SD card in the slot. Attempt to initialize
+ * and configure the SD card. This is called only from mmcsd_probe().
+ *
+ ****************************************************************************/
+
+static int mmcsd_sdinitialize(FAR struct mmcsd_state_s *priv)
+{
+ uint32_t cid[4];
+ uint32_t csd[4];
+ uint32_t scr[2];
+ int ret;
+
+ /* At this point, clocking has been supplied to the card, both CMD0 and
+ * ACMD41 (with OCR=0) have been sent successfully, the card is no longer
+ * busy and (presumably) in the IDLE state so there is good evidence
+ * that we have an SD card inserted into the slot.
+ *
+ * Send CMD2, ALL_SEND_CID. The SD CMD2 is similar to the MMC CMD2 except
+ * that the buffer type used to transmit to response of the card (SD Memory
+ * Card: Push-Pull, MMC: Open-Drain). This implementation supports only a
+ * single SD card. If multiple cards were installed in the slot, each card
+ * would respond to CMD2 by sending its CID (only one card completes the
+ * response at a time). The driver should send CMD2 and obtain RCAs until
+ * no response to ALL_SEND_CID is received.
+ *
+ * When an SD card receives the CMD2 command it should transition to the
+ * identification state/card-identification mode
+ */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD2, 0);
+ ret = SDIO_RECVR2(priv->dev, MMCSD_CMD2, cid);
+ if (ret != OK)
+ {
+ fdbg("ERROR: SDIO_RECVR2 for SD CID failed: %d\n", ret);
+ return ret;
+ }
+ mmcsd_decodeCID(priv, cid);
+
+ /* Send CMD3, SET_RELATIVE_ADDR. In both protocols, this command is used
+ * to assign a logical address to the card. For MMC, the host assigns the
+ * address; for SD, the memory card has this responsibility. CMD3 causes
+ * transition to standby state/data-transfer mode
+ *
+ * Send CMD3 with argument 0, SD card publishes its RCA in the response.
+ */
+
+ mmcsd_sendcmdpoll(priv, SD_CMD3, 0);
+ ret = mmcsd_recvR6(priv, SD_CMD3);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_recvR2 for SD RCA failed: %d\n", ret);
+ return ret;
+ }
+ fvdbg("RCA: %04x\n", priv->rca);
+
+ /* This should have caused a transition to standby state. However, this will
+ * not be reflected in the present R1 status. R1/6 contains the state of
+ * the card when the command was received, not when it completed execution.
+ *
+ * Verify that we are in standby state/data-transfer mode
+ */
+
+ ret = mmcsd_verifystate(priv, MMCSD_R1_STATE_STBY);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Failed to enter standby state\n");
+ return ret;
+ }
+
+ /* Send CMD9, SEND_CSD, in standby state/data-transfer mode to obtain the
+ * Card Specific Data (CSD) register. The argument is the RCA that we
+ * just obtained from CMD3. The card stays in standy state/data-transfer
+ * mode.
+ */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD9, (uint32_t)priv->rca << 16);
+ ret = SDIO_RECVR2(priv->dev, MMCSD_CMD9, csd);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Could not get SD CSD register(%d)\n", ret);
+ return ret;
+ }
+ mmcsd_decodeCSD(priv, csd);
+
+ /* Send CMD7 with the argument == RCA in order to select the card.
+ * Since we are supporting only a single card, we just leave the
+ * card selected all of the time.
+ */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD7S, (uint32_t)priv->rca << 16);
+ ret = mmcsd_recvR1(priv, MMCSD_CMD7S);
+ if (ret != OK)
+ {
+ fdbg("ERROR: mmcsd_recvR1 for CMD7 failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Set the Driver Stage Register (DSR) if (1) a CONFIG_MMCSD_DSR has been
+ * provided and (2) the card supports a DSR register. If no DSR value
+ * the card default value (0x0404) will be used.
+ */
+
+ (void)mmcsd_sendcmd4(priv);
+
+ /* Select high speed SD clocking (which may depend on the DSR setting) */
+
+ SDIO_CLOCK(priv->dev, CLOCK_SD_TRANSFER_1BIT);
+ up_udelay(MMCSD_CLK_DELAY);
+
+ /* Get the SD card Configuration Register (SCR). We need this now because
+ * that configuration register contains the indication whether or not
+ * this card supports wide bus operation.
+ */
+
+ ret = mmcsd_getSCR(priv, scr);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Could not get SD SCR register(%d)\n", ret);
+ return ret;
+ }
+ mmcsd_decodeSCR(priv, scr);
+
+ /* Select width (4-bit) bus operation (if the card supports it) */
+
+ ret = mmcsd_widebus(priv);
+ if (ret != OK)
+ {
+ fdbg("WARN: Failed to set wide bus operation: %d\n", ret);
+ }
+
+ /* TODO: If widebus selected, then send CMD6 to see if the card supports
+ * high speed mode. A new SDIO method will be needed to set high speed
+ * mode.
+ */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mmcsd_cardidentify
+ *
+ * Description:
+ * We believe that there is media in the slot. Attempt to initialize and
+ * configure the card. This is called only from mmcsd_probe().
+ *
+ ****************************************************************************/
+
+static int mmcsd_cardidentify(FAR struct mmcsd_state_s *priv)
+{
+ uint32_t response;
+ uint32_t start;
+ uint32_t elapsed;
+ uint32_t sdcapacity = MMCSD_ACMD41_STDCAPACITY;
+ int ret;
+
+ /* Assume failure to identify the card */
+
+ priv->type = MMCSD_CARDTYPE_UNKNOWN;
+
+ /* Check if there is a card present in the slot. This is normally a matter is
+ * of GPIO sensing.
+ */
+
+ if (!SDIO_PRESENT(priv->dev))
+ {
+ fvdbg("No card present\n");
+ return -ENODEV;
+ }
+
+ /* Set ID mode clocking (<400KHz) */
+
+ SDIO_CLOCK(priv->dev, CLOCK_IDMODE);
+
+ /* After power up at least 74 clock cycles are required prior to starting bus
+ * communication
+ */
+
+ up_udelay(MMCSD_POWERUP_DELAY);
+
+ /* Then send CMD0 (twice just to be sure) */
+
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD0, 0);
+ mmcsd_sendcmdpoll(priv, MMCSD_CMD0, 0);
+ up_udelay(MMCSD_IDLE_DELAY);
+
+ /* Check for SDHC Version 2.x. Send CMD8 to verify SD card interface
+ * operating condition. CMD 8 is reserved on SD version 1.0 and MMC.
+ *
+ * CMD8 Argument:
+ * [31:12]: Reserved (shall be set to '0') * [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)
+ * [7:0]: Check Pattern (recommended 0xaa)
+ * CMD8 Response: R7
+ */
+
+ ret = mmcsd_sendcmdpoll(priv, SD_CMD8, MMCSD_CMD8CHECKPATTERN|MMCSD_CMD8VOLTAGE_27);
+ if (ret == OK)
+ {
+ /* CMD8 was sent successfully... Get the R7 response */
+
+ ret = SDIO_RECVR7(priv->dev, SD_CMD8, &response);
+ }
+
+ /* Were both the command sent and response received correctly? */
+
+ if (ret == OK)
+ {
+ /* CMD8 succeeded this is probably a SDHC card. Verify the operating
+ * voltage and that the check pattern was correctly echoed
+ */
+
+ if (((response & MMCSD_R7VOLTAGE_MASK) == MMCSD_R7VOLTAGE_27) &&
+ ((response & MMCSD_R7ECHO_MASK) == MMCSD_R7CHECKPATTERN))
+ {
+ fvdbg("SD V2.x card\n");
+ priv->type = MMCSD_CARDTYPE_SDV2;
+ sdcapacity = MMCSD_ACMD41_HIGHCAPACITY;
+ }
+ else
+ {
+ fdbg("ERROR: R7: %08x\n", response);
+ return -EIO;
+ }
+ }
+
+ /* At this point, type is either UNKNOWN or SDV2. Try sending
+ * CMD55 and (maybe) ACMD41 for up to 1 second or until the card
+ * exits the IDLE state. CMD55 is supported by SD V1.x and SD V2.x,
+ * but not MMC
+ */
+
+ start = clock_systimer();
+ elapsed = 0;
+ do
+ {
+ /* We may have already determined that his card is an MMC card from
+ * an earlier pass through through this loop. In that case, we should
+ * skip the SD-specific commands.
+ */
+
+#ifdef CONFIG_MMCSD_MMCSUPPORT
+ if (priv->type != MMCSD_CARDTYPE_MMC)
+#endif
+ {
+ /* Send CMD55 with argument = 0 */
+
+ mmcsd_sendcmdpoll(priv, SD_CMD55, 0);
+ ret = mmcsd_recvR1(priv, SD_CMD55);
+ if (ret != OK)
+ {
+ /* I am a little confused.. I think both SD and MMC cards support
+ * CMD55 (but maybe only SD cards support CMD55). We'll make the
+ * the MMC vs. SD decision based on CMD1 and ACMD41.
+ */
+
+ fdbg("ERROR: mmcsd_recvR1(CMD55) failed: %d\n", ret);
+ }
+ else
+ {
+ /* Send ACMD41 */
+
+ mmcsd_sendcmdpoll(priv, SD_ACMD41, MMCSD_ACMD41_VOLTAGEWINDOW|sdcapacity);
+ ret = SDIO_RECVR3(priv->dev, SD_ACMD41, &response);
+ if (ret != OK)
+ {
+ /* If the error is a timeout, then it is probably an MMC card,
+ * but we will make the decision based on CMD1 below
+ */
+
+ fdbg("ERROR: ACMD41 RECVR3: %d\n", ret);
+ }
+ else
+ {
+ /* ACMD41 succeeded. ACMD41 is supported by SD V1.x and SD V2.x,
+ * but not MMC. If we did not previously determine that this is
+ * an SD V2.x (via CMD8), then this must be SD V1.x
+ */
+
+ fvdbg("R3: %08x\n", response);
+ if (priv->type == MMCSD_CARDTYPE_UNKNOWN)
+ {
+ fvdbg("SD V1.x card\n");
+ priv->type = MMCSD_CARDTYPE_SDV1;
+ }
+
+ /* Check if the card is busy. Very confusing, BUSY is set LOW
+ * if the card has not finished its initialization, so it really
+ * means NOT busy.
+ */
+
+ if ((response & MMCSD_CARD_BUSY) != 0)
+ {
+ /* No.. We really should check the current state to see if
+ * the SD card successfully made it to the IDLE state, but
+ * at least for now, we will simply assume that that is the
+ * case.
+ *
+ * Now, check if this is a SD V2.x card that supports block
+ * addressing
+ */
+
+ if ((response & MMCSD_R3_HIGHCAPACITY) != 0)
+ {
+ fvdbg("SD V2.x card with block addressing\n");
+ DEBUGASSERT(priv->type == MMCSD_CARDTYPE_SDV2);
+ priv->type |= MMCSD_CARDTYPE_BLOCK;
+ }
+
+ /* And break out of the loop with an SD card identified */
+
+ break;
+ }
+ }
+ }
+ }
+
+ /* If we get here then either (1) CMD55 failed, (2) CMD41 failed, or (3)
+ * and SD or MMC card has been identified, but it is not yet in the IDLE state.
+ * If SD card has not been identified, then we might be looking at an
+ * MMC card. We can send the CMD1 to find out for sure. CMD1 is supported
+ * by MMC cards, but not by SD cards.
+ */
+#ifdef CONFIG_MMCSD_MMCSUPPORT
+ if (priv->type == MMCSD_CARDTYPE_UNKNOWN || priv->type == MMCSD_CARDTYPE_MMC)
+ {
+ /* Send the MMC CMD1 to specify the operating voltage. CMD1 causes
+ * transition to ready state/ card-identification mode. NOTE: If the
+ * card does not support this voltage range, it will go the inactive
+ * state.
+ *
+ * NOTE: An MMC card will only respond once to CMD1 (unless it is busy).
+ * This is part of the logic used to determine how many MMC cards are
+ * connected (This implementation supports only a single MMC card). So
+ * we cannot re-send CMD1 without first placing the card back into
+ * stand-by state (if the card is busy, it will automatically
+ * go back to the the standby state).
+ */
+
+ mmcsd_sendcmdpoll(priv, MMC_CMD1, MMCSD_VDD_33_34);
+ ret = SDIO_RECVR3(priv->dev, MMC_CMD1, &response);
+
+ /* Was the operating range set successfully */
+
+ if (ret != OK)
+ {
+ fdbg("ERROR: CMD1 RECVR3: %d\n", ret);
+ }
+ else
+ {
+ /* CMD1 succeeded... this must be an MMC card */
+
+ fdbg("CMD1 succeeded, assuming MMC card\n");
+ priv->type = MMCSD_CARDTYPE_MMC;
+
+ /* Check if the card is busy. Very confusing, BUSY is set LOW
+ * if the card has not finished its initialization, so it really
+ * means NOT busy.
+ */
+
+ if ((response & MMCSD_CARD_BUSY) != 0)
+ {
+ /* NO.. We really should check the current state to see if the
+ * MMC successfully made it to the IDLE state, but at least for now,
+ * we will simply assume that that is the case.
+ *
+ * Then break out of the look with an MMC card identified
+ */
+
+ break;
+ }
+ }
+ }
+#endif
+ /* Check the elapsed time. We won't keep trying this forever! */
+
+ elapsed = clock_systimer() - start;
+ }
+ while( elapsed < TICK_PER_SEC ); /* On successful reception while 'breaks', see above. */
+
+ /* We get here when the above loop completes, either (1) we could not
+ * communicate properly with the card due to errors (and the loop times
+ * out), or (3) it is an MMC or SD card that has successfully transitioned
+ * to the IDLE state (well, at least, it provided its OCR saying that it
+ * it is no longer busy).
+ */
+
+ if (elapsed >= TICK_PER_SEC || priv->type == MMCSD_CARDTYPE_UNKNOWN)
+ {
+ fdbg("ERROR: Failed to identify card\n");
+ return -EIO;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mmcsd_probe
+ *
+ * Description:
+ * Check for media inserted in a slot. Called (1) during initialization to
+ * see if there was a card in the slot at power up, (2) when/if a media
+ * insertion event occurs, or (3) if the BIOC_PROBE ioctl command is
+ * received.
+ *
+ ****************************************************************************/
+
+static int mmcsd_probe(FAR struct mmcsd_state_s *priv)
+{
+ int ret;
+
+ fvdbg("type: %d probed: %d\n", priv->type, priv->probed);
+
+ /* If we have reliable card detection events and if we have
+ * already probed the card, then we don't need to do anything
+ * else
+ */
+
+#ifdef CONFIG_MMCSD_HAVECARDDETECT
+ if (priv->probed && SDIO_PRESENT(priv->dev))
+ {
+ return OK;
+ }
+#endif
+
+ /* Otherwise, we are going to probe the card. There are lots of
+ * possibilities here: We may think that there is a card in the slot,
+ * or not. There may be a card in the slot, or not. If there is
+ * card in the slot, perhaps it is a different card than we one we
+ * think is there? The safest thing to do is to process the card
+ * removal first and start from known place.
+ */
+
+ mmcsd_removed(priv);
+
+ /* Now.. is there a card in the slot? */
+
+ if (SDIO_PRESENT(priv->dev))
+ {
+ /* Yes.. probe it. First, what kind of card was inserted? */
+
+ ret = mmcsd_cardidentify(priv);
+ if (ret != OK)
+ {
+ fdbg("ERROR: Failed to initialize card: %d\n", ret);
+#ifdef CONFIG_MMCSD_HAVECARDDETECT
+ SDIO_CALLBACKENABLE(priv->dev, SDIOMEDIA_INSERTED);
+#endif
+ }
+ else
+ {
+ /* Then initialize the driver according to the identified card type */
+
+ switch (priv->type)
+ {
+ case MMCSD_CARDTYPE_SDV1: /* Bit 1: SD version 1.x */
+ case MMCSD_CARDTYPE_SDV2: /* SD version 2.x with byte addressing */
+ case MMCSD_CARDTYPE_SDV2|MMCSD_CARDTYPE_BLOCK: /* SD version 2.x with block addressing */
+ ret = mmcsd_sdinitialize(priv);
+ break;
+
+ case MMCSD_CARDTYPE_MMC: /* MMC card */
+#ifdef CONFIG_MMCSD_MMCSUPPORT
+ ret = mmcsd_mmcinitialize(priv);
+ break;
+#endif
+ case MMCSD_CARDTYPE_UNKNOWN: /* Unknown card type */
+ default:
+ fdbg("ERROR: Internal confusion: %d\n", priv->type);
+ ret = -EPERM;
+ break;
+ };
+
+ /* Was the card configured successfully? */
+
+ if (ret == OK)
+ {
+ /* Yes... */
+
+ fvdbg("Capacity: %lu Kbytes\n", (unsigned long)(priv->capacity / 1024));
+ priv->mediachanged = true;
+
+ /* Set up to receive asynchronous, media removal events */
+
+ SDIO_CALLBACKENABLE(priv->dev, SDIOMEDIA_EJECTED);
+ }
+ }
+
+ /* In any event, we have probed this card */
+
+ priv->probed = true;
+ }
+ else
+ {
+ /* There is no card in the slot */
+
+ fvdbg("No card\n");
+#ifdef CONFIG_MMCSD_HAVECARDDETECT
+ SDIO_CALLBACKENABLE(priv->dev, SDIOMEDIA_INSERTED);
+#endif
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_removed
+ *
+ * Description:
+ * Disable support for media in the slot. Called (1) when/if a media
+ * removal event occurs, or (2) if the BIOC_EJECT ioctl command is
+ * received.
+ *
+ ****************************************************************************/
+
+static int mmcsd_removed(FAR struct mmcsd_state_s *priv)
+{
+ fvdbg("type: %d present: %d\n", priv->type, SDIO_PRESENT(priv->dev));
+
+ /* Forget the card geometry, pretend the slot is empty (it might not
+ * be), and that the card has never been initialized.
+ */
+
+ priv->capacity = 0; /* Capacity=0 sometimes means no media */
+ priv->blocksize = 0;
+ priv->mediachanged = false;
+ priv->type = MMCSD_CARDTYPE_UNKNOWN;
+ priv->probed = false;
+ priv->rca = 0;
+ priv->selblocklen = 0;
+
+ /* Go back to the default 1-bit data bus. */
+
+ SDIO_WIDEBUS(priv->dev, false);
+ priv->widebus = false;
+
+ /* Disable clocking to the card */
+
+ (void)SDIO_CLOCK(priv->dev, CLOCK_SDIO_DISABLED);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mmcsd_hwinitialize
+ *
+ * Description:
+ * One-time hardware initialization. Called only from sdio_slotinitialize.
+ *
+ ****************************************************************************/
+
+static int mmcsd_hwinitialize(FAR struct mmcsd_state_s *priv)
+{
+ int ret;
+
+ mmcsd_takesem(priv);
+
+#ifdef CONFIG_SDIO_DMA
+ /* Does this architecture support DMA with the MMC/SD device? */
+
+ priv->dma = SDIO_DMASUPPORTED(priv->dev);
+ fvdbg("DMA supported: %d\n", priv->dma);
+#endif
+
+ /* Attach and prepare MMC/SD interrupts */
+
+ if (SDIO_ATTACH(priv->dev))
+ {
+ fdbg("ERROR: Unable to attach MMC/SD interrupts\n");
+ mmcsd_givesem(priv);
+ return -EBUSY;
+ }
+ fvdbg("Attached MMC/SD interrupts\n");
+
+ /* Register a callback so that we get informed if media is inserted or
+ * removed from the slot (Initially all callbacks are disabled).
+ */
+
+ SDIO_REGISTERCALLBACK(priv->dev, mmcsd_mediachange, (FAR void *)priv);
+
+ /* Is there a card in the slot now? For an MMC/SD card, there are three
+ * possible card detect mechanisms:
+ *
+ * 1. Mechanical insertion that can be detected using the WP switch
+ * that is closed when a card is inserted into then SD slot (SD
+ * "hot insertion capable" card conector only)
+ * 2. Electrical insertion that can be sensed using the pull-up resistor
+ * on CD/DAT3 (both SD/MMC),
+ * 3. Or by periodic attempts to initialize the card from software.
+ *
+ * The behavior of SDIO_PRESENT() is to use whatever information is available
+ * on the particular platform. If no card insertion information is available
+ * (polling only), then SDIO_PRESENT() will always return true and we will
+ * try to initialize the card.
+ */
+
+ if (SDIO_PRESENT(priv->dev))
+ {
+ /* Yes... probe for a card in the slot */
+
+ ret = mmcsd_probe(priv);
+ if (ret != OK)
+ {
+ fvdbg("Slot not empty, but initialization failed: %d\n", ret);
+
+ /* NOTE: The failure to initialize a card does not mean that
+ * initialization has failed! A card could be installed in the slot
+ * at a later time. ENODEV is return in this case,
+ * sdio_slotinitialize will use this return value to set up the
+ * card inserted callback event.
+ */
+
+ ret = -ENODEV;
+ }
+ }
+ else
+ {
+ /* ENODEV is returned to indicate that no card is inserted in the slot.
+ * sdio_slotinitialize will use this return value to set up the card
+ * inserted callback event.
+ */
+
+ ret = -ENODEV;
+ }
+
+ /* OK is returned only if the slot initialized correctly AND the card in
+ * the slot was successfully configured.
+ */
+
+ mmcsd_givesem(priv);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_hwuninitialize
+ *
+ * Description:
+ * Restore the MMC/SD slot to the uninitialized state. Called only from
+ * sdio_slotinitialize on a failure to initialize.
+ *
+ ****************************************************************************/
+
+static void mmcsd_hwuninitialize(FAR struct mmcsd_state_s *priv)
+{
+ if (priv)
+ {
+ mmcsd_removed(priv);
+ SDIO_RESET(priv->dev);
+ kfree(priv);
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mmcsd_slotinitialize
+ *
+ * Description:
+ * Initialize one slot for operation using the MMC/SD interface
+ *
+ * Input Parameters:
+ * minor - The MMC/SD minor device number. The MMC/SD device will be
+ * registered as /dev/mmcsdN where N is the minor number
+ * dev - And instance of an MMC/SD interface. The MMC/SD hardware should
+ * be initialized and ready to use.
+ *
+ ****************************************************************************/
+
+int mmcsd_slotinitialize(int minor, FAR struct sdio_dev_s *dev)
+{
+ FAR struct mmcsd_state_s *priv;
+ char devname[16];
+ int ret = -ENOMEM;
+
+ fvdbg("minor: %d\n", minor);
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (minor < 0 || minor > 255 || !dev)
+ {
+ return -EINVAL;
+ }
+#endif
+
+ /* Allocate a MMC/SD state structure */
+
+ priv = (FAR struct mmcsd_state_s *)kmalloc(sizeof(struct mmcsd_state_s));
+ if (priv)
+ {
+ /* Initialize the MMC/SD state structure */
+
+ memset(priv, 0, sizeof(struct mmcsd_state_s));
+ sem_init(&priv->sem, 0, 1);
+
+ /* Bind the MMCSD driver to the MMCSD state structure */
+
+ priv->dev = dev;
+
+ /* Initialize the hardware associated with the slot */
+
+ ret = mmcsd_hwinitialize(priv);
+
+ /* Was the slot initialized successfully? */
+
+ if (ret != OK)
+ {
+ /* No... But the error ENODEV is returned if hardware initialization
+ * succeeded but no card is inserted in the slot. In this case, the
+ * no error occurred, but the driver is still not ready.
+ */
+
+ if (ret == -ENODEV)
+ {
+ /* No card in the slot (or if there is, we could not recognize
+ * it).. Setup to receive the media inserted event
+ */
+
+ SDIO_CALLBACKENABLE(priv->dev, SDIOMEDIA_INSERTED);
+
+ fdbg("MMC/SD slot is empty\n");
+ }
+ else
+ {
+ /* Some other non-recoverable bad thing happened */
+
+ fdbg("ERROR: Failed to initialize MMC/SD slot: %d\n", ret);
+ goto errout_with_alloc;
+ }
+ }
+
+ /* Initialize buffering */
+
+#if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD)
+
+ ret = rwb_initialize(&priv->rwbuffer);
+ if (ret < 0)
+ {
+ fdbg("ERROR: Buffer setup failed: %d\n", ret);
+ goto errout_with_hwinit;
+ }
+#endif
+
+ /* Create a MMCSD device name */
+
+ snprintf(devname, 16, "/dev/mmcsd%d", minor);
+
+ /* Inode private data is a reference to the MMCSD state structure */
+
+ ret = register_blockdriver(devname, &g_bops, 0, priv);
+ if (ret < 0)
+ {
+ fdbg("ERROR: register_blockdriver failed: %d\n", ret);
+ goto errout_with_buffers;
+ }
+ }
+ return OK;
+
+errout_with_buffers:
+#if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD)
+ rwb_uninitialize(&priv->rwbuffer);
+errout_with_hwinit:
+#endif
+ mmcsd_hwuninitialize(priv); /* This will free the private data structure */
+ return ret;
+
+errout_with_alloc:
+ kfree(priv);
+ return ret;
+}
diff --git a/nuttx/drivers/mmcsd/mmcsd_sdio.h b/nuttx/drivers/mmcsd/mmcsd_sdio.h
new file mode 100644
index 000000000..9e3794e25
--- /dev/null
+++ b/nuttx/drivers/mmcsd/mmcsd_sdio.h
@@ -0,0 +1,339 @@
+/********************************************************************************************
+ * drivers/mmcsd/mmcsd_sdio.h
+ *
+ * Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ********************************************************************************************/
+
+#ifndef __DRIVERS_MMCSD_MMCSD_SDIO_H
+#define __DRIVERS_MMCSD_MMCSD_SDIO_H
+
+/********************************************************************************************
+ * Included Files
+ ********************************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+
+/********************************************************************************************
+ * Pre-Processor Definitions
+ ********************************************************************************************/
+
+/* CMD8 Argument:
+ * [31:12]: Reserved (shall be set to '0')
+ * [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)
+ * [7:0]: Check Pattern (recommended 0xaa)
+ * CMD8 Response: R7
+ */
+
+#define MMCSD_CMD8VOLTAGE_SHIFT (8) /* Bits 8-11: Supply voltage */
+#define MMCSD_CMD8VOLTAGE_MASK ((uint32_t)0x0f << MMCSD_CMD8VOLTAGE_SHIFT)
+# define MMCSD_CMD8VOLTAGE_27 ((uint32_t)0x01 << MMCSD_CMD8VOLTAGE_SHIFT) /* 2.7-3.6V */
+#define MMCSD_CMD8ECHO_SHIFT (0) /* Bits 0-7: Check pattern */
+#define MMCSD_CMD8ECHO_MASK ((uint32_t)0xff << MMCSD_CMD8ECHO_SHIFT)
+# define MMCSD_CMD8CHECKPATTERN ((uint32_t)0xaa << MMCSD_CMD8ECHO_SHIFT)
+
+/* ACMD6 argument */
+
+#define MMCSD_ACMD6_BUSWIDTH_1 ((uint32_t)0) /* Bus width = 1-bit */
+#define MMCSD_ACMD6_BUSWIDTH_4 ((uint32_t)2) /* Bus width = 4-bit */
+
+/* ACMD41 argument */
+
+#define MMCSD_ACMD41_VOLTAGEWINDOW ((uint32_t)0x80100000)
+#define MMCSD_ACMD41_HIGHCAPACITY ((uint32_t)1 << 30)
+#define MMCSD_ACMD41_STDCAPACITY ((uint32_t)0)
+
+/* ACMD42 argument */
+
+#define MMCSD_ACMD42_CD_DISCONNECT ((uint32_t)0) /* Disconnect card detection logic */
+#define MMCSD_ACMD42_CD_CONNECT ((uint32_t)1) /* Connect card detection logic */
+
+/* R1 Card Status bit definitions */
+
+#define MMCSD_R1_OUTOFRANGE ((uint32_t)1 << 31) /* Bad argument */
+#define MMCSD_R1_ADDRESSERROR ((uint32_t)1 << 30) /* Bad address */
+#define MMCSD_R1_BLOCKLENERROR ((uint32_t)1 << 29) /* Bad block length */
+#define MMCSD_R1_ERASESEQERROR ((uint32_t)1 << 28) /* Erase cmd error */
+#define MMCSD_R1_ERASEPARAM ((uint32_t)1 << 27) /* Bad write blocks */
+#define MMCSD_R1_WPVIOLATION ((uint32_t)1 << 26) /* Erase access failure */
+#define MMCSD_R1_CARDISLOCKED ((uint32_t)1 << 25) /* Card is locked */
+#define MMCSD_R1_LOCKUNLOCKFAILED ((uint32_t)1 << 24) /* Password error */
+#define MMCSD_R1_COMCRCERROR ((uint32_t)1 << 23) /* CRC error */
+#define MMCSD_R1_ILLEGALCOMMAND ((uint32_t)1 << 22) /* Bad command */
+#define MMCSD_R1_CARDECCFAILED ((uint32_t)1 << 21) /* Failed to correct data */
+#define MMCSD_R1_CCERROR ((uint32_t)1 << 20) /* Card controller error */
+#define MMCSD_R1_ERROR ((uint32_t)1 << 19) /* General error */
+#define MMCSD_R1_UNDERRUN ((uint32_t)1 << 18) /* Underrun (MMC only) */
+#define MMCSD_R1_OVERRRUN ((uint32_t)1 << 17) /* Overrun (MMC only) */
+#define MMCSD_R1_CIDCSDOVERWRITE ((uint32_t)1 << 16) /* CID/CSD error */
+#define MMCSD_R1_WPERASESKIP ((uint32_t)1 << 15) /* Not all erased */
+#define MMCSD_R1_CARDECCDISABLED ((uint32_t)1 << 14) /* Internal ECC not used */
+#define MMCSD_R1_ERASERESET ((uint32_t)1 << 13) /* Reset sequence cleared */
+#define MMCSD_R1_STATE_SHIFT (9) /* Current card state */
+#define MMCSD_R1_STATE_MASK ((uint32_t)15 << MMCSD_R1_STATE_SHIFT)
+ /* Card identification mode states */
+# define MMCSD_R1_STATE_IDLE ((uint32_t)0 << MMCSD_R1_STATE_SHIFT) /* 0=Idle state */
+# define MMCSD_R1_STATE_READY ((uint32_t)1 << MMCSD_R1_STATE_SHIFT) /* 1=Ready state */
+# define MMCSD_R1_STATE_IDENT ((uint32_t)2 << MMCSD_R1_STATE_SHIFT) /* 2=Identification state */
+ /* Data transfer states */
+# define MMCSD_R1_STATE_STBY ((uint32_t)3 << MMCSD_R1_STATE_SHIFT) /* 3=Standby state */
+# define MMCSD_R1_STATE_TRAN ((uint32_t)4 << MMCSD_R1_STATE_SHIFT) /* 4=Transfer state */
+# define MMCSD_R1_STATE_DATA ((uint32_t)5 << MMCSD_R1_STATE_SHIFT) /* 5=Sending data state */
+# define MMCSD_R1_STATE_RCV ((uint32_t)6 << MMCSD_R1_STATE_SHIFT) /* 6=Receiving data state */
+# define MMCSD_R1_STATE_PRG ((uint32_t)7 << MMCSD_R1_STATE_SHIFT) /* 7=Programming state */
+# define MMCSD_R1_STATE_DIS ((uint32_t)8 << MMCSD_R1_STATE_SHIFT) /* 8=Disconnect state */
+#define MMCSD_R1_READYFORDATA ((uint32_t)1 << 8) /* Buffer empty */
+#define MMCSD_R1_APPCMD ((uint32_t)1 << 5) /* Next CMD is ACMD */
+#define MMCSD_R1_AKESEQERROR ((uint32_t)1 << 3) /* Authentication error */
+#define MMCSD_R1_ERRORMASK ((uint32_t)0xfdffe008) /* Error mask */
+
+#define IS_STATE(v,s) ((((uint32_t)v)&MMCSD_R1_STATE_MASK)==(s))
+
+/* R3 (OCR) */
+
+#define MMC_VDD_20_36 ((uint32_t)0x00ffff00) /* VDD voltage 2.0-3.6 */
+
+#define MMCSD_VDD_145_150 ((uint32_t)1 << 0) /* VDD voltage 1.45 - 1.50 */
+#define MMCSD_VDD_150_155 ((uint32_t)1 << 1) /* VDD voltage 1.50 - 1.55 */
+#define MMCSD_VDD_155_160 ((uint32_t)1 << 2) /* VDD voltage 1.55 - 1.60 */
+#define MMCSD_VDD_160_165 ((uint32_t)1 << 3) /* VDD voltage 1.60 - 1.65 */
+#define MMCSD_VDD_165_170 ((uint32_t)1 << 4) /* VDD voltage 1.65 - 1.70 */
+#define MMCSD_VDD_17_18 ((uint32_t)1 << 5) /* VDD voltage 1.7 - 1.8 */
+#define MMCSD_VDD_18_19 ((uint32_t)1 << 6) /* VDD voltage 1.8 - 1.9 */
+#define MMCSD_VDD_19_20 ((uint32_t)1 << 7) /* VDD voltage 1.9 - 2.0 */
+#define MMCSD_VDD_20_21 ((uint32_t)1 << 8) /* VDD voltage 2.0-2.1 */
+#define MMCSD_VDD_21_22 ((uint32_t)1 << 9) /* VDD voltage 2.1-2.2 */
+#define MMCSD_VDD_22_23 ((uint32_t)1 << 10) /* VDD voltage 2.2-2.3 */
+#define MMCSD_VDD_23_24 ((uint32_t)1 << 11) /* VDD voltage 2.3-2.4 */
+#define MMCSD_VDD_24_25 ((uint32_t)1 << 12) /* VDD voltage 2.4-2.5 */
+#define MMCSD_VDD_25_26 ((uint32_t)1 << 13) /* VDD voltage 2.5-2.6 */
+#define MMCSD_VDD_26_27 ((uint32_t)1 << 14) /* VDD voltage 2.6-2.7 */
+#define MMCSD_VDD_27_28 ((uint32_t)1 << 15) /* VDD voltage 2.7-2.8 */
+#define MMCSD_VDD_28_29 ((uint32_t)1 << 16) /* VDD voltage 2.8-2.9 */
+#define MMCSD_VDD_29_30 ((uint32_t)1 << 17) /* VDD voltage 2.9-3.0 */
+#define MMCSD_VDD_30_31 ((uint32_t)1 << 18) /* VDD voltage 3.0-3.1 */
+#define MMCSD_VDD_31_32 ((uint32_t)1 << 19) /* VDD voltage 3.1-3.2 */
+#define MMCSD_VDD_32_33 ((uint32_t)1 << 20) /* VDD voltage 3.2-3.3 */
+#define MMCSD_VDD_33_34 ((uint32_t)1 << 21) /* VDD voltage 3.3-3.4 */
+#define MMCSD_VDD_34_35 ((uint32_t)1 << 22) /* VDD voltage 3.4-3.5 */
+#define MMCSD_VDD_35_36 ((uint32_t)1 << 23) /* VDD voltage 3.5-3.6 */
+#define MMCSD_R3_HIGHCAPACITY ((uint32_t)1 << 30) /* true: Card supports block addressing */
+#define MMCSD_CARD_BUSY ((uint32_t)1 << 31) /* Card power-up busy bit */
+
+/* R6 Card Status bit definitions */
+
+#define MMCSD_R6_RCA_SHIFT (16) /* New published RCA */
+#define MMCSD_R6_RCA_MASK ((uint32_t)0xffff << MMCSD_R6_RCA_SHIFT)
+#define MMCSD_R6_COMCRCERROR ((uint32_t)1 << 15) /* CRC error */
+#define MMCSD_R6_ILLEGALCOMMAND ((uint32_t)1 << 14) /* Bad command */
+#define MMCSD_R6_ERROR ((uint32_t)1 << 13) /* General error */
+#define MMCSD_R6_STATE_SHIFT (9) /* Current card state */
+#define MMCSD_R6_STATE_MASK ((uint32_t)15 << MMCSD_R6_STATE_SHIFT)
+ /* Card identification mode states */
+# define MMCSD_R6_STATE_IDLE ((uint32_t)0 << MMCSD_R6_STATE_SHIFT) /* 0=Idle state */
+# define MMCSD_R6_STATE_READY ((uint32_t)1 << MMCSD_R6_STATE_SHIFT) /* 1=Ready state */
+# define MMCSD_R6_STATE_IDENT ((uint32_t)2 << MMCSD_R6_STATE_SHIFT) /* 2=Identification state */
+ /* Data transfer states */
+# define MMCSD_R6_STATE_STBY ((uint32_t)3 << MMCSD_R6_STATE_SHIFT) /* 3=Standby state */
+# define MMCSD_R6_STATE_TRAN ((uint32_t)4 << MMCSD_R6_STATE_SHIFT) /* 4=Transfer state */
+# define MMCSD_R6_STATE_DATA (5(uint32_t) << MMCSD_R6_STATE_SHIFT) /* 5=Sending data state */
+# define MMCSD_R6_STATE_RCV ((uint32_t)6 << MMCSD_R6_STATE_SHIFT) /* 6=Receiving data state */
+# define MMCSD_R6_STATE_PRG ((uint32_t)7 << MMCSD_R6_STATE_SHIFT) /* 7=Programming state */
+# define MMCSD_R6_STATE_DIS ((uint32_t) << MMCSD_R6_STATE_SHIFT) /* 8=Disconnect state */
+#define MMCSD_R6_ERRORMASK ((uint32_t)0x0000e000) /* Error mask */
+
+/* SD Configuration Register (SCR) encoding */
+
+#define MMCSD_SCR_BUSWIDTH_1BIT (1)
+#define MMCSD_SCR_BUSWIDTH_2BIT (2)
+#define MMCSD_SCR_BUSWIDTH_4BIT (4)
+#define MMCSD_SCR_BUSWIDTH_8BIT (8)
+
+/* Last 4 bytes of the 48-bit R7 response */
+
+#define MMCSD_R7VERSION_SHIFT (28) /* Bits 28-31: Command version number */
+#define MMCSD_R7VERSION_MASK ((uint32_t)0x0f << MMCSD_R7VERSION_SHIFT)
+#define MMCSD_R7VOLTAGE_SHIFT (8) /* Bits 8-11: Voltage accepted */
+#define MMCSD_R7VOLTAGE_MASK ((uint32_t)0x0f << MMCSD_R7VOLTAGE_SHIFT)
+# define MMCSD_R7VOLTAGE_27 ((uint32_t)0x01 << MMCSD_R7VOLTAGE_SHIFT) /* 2.7-3.6V */
+#define MMCSD_R7ECHO_SHIFT (0) /* Bits 0-7: Echoed check pattern */
+#define MMCSD_R7ECHO_MASK ((uint32_t)0xff << MMCSD_R7ECHO_SHIFT)
+# define MMCSD_R7CHECKPATTERN ((uint32_t)0xaa << MMCSD_R7ECHO_SHIFT)
+
+/********************************************************************************************
+ * Public Types
+ ********************************************************************************************/
+
+/* Decoded Card Identification (CID) register */
+
+struct mmcsd_cid_s
+{
+ uint8_t mid; /* 127:120 8-bit Manufacturer ID */
+ uint16_t oid; /* 119:104 16-bit OEM/Application ID (ascii) */
+ uint8_t pnm[6]; /* 103:64 40-bit Product Name (ascii) + null terminator */
+ uint8_t prv; /* 63:56 8-bit Product revision */
+ uint32_t psn; /* 55:24 32-bit Product serial number */
+ /* 23:20 4-bit (reserved) */
+ uint16_t mdt; /* 19:8 12-bit Manufacturing date */
+ uint8_t crc; /* 7:1 7-bit CRC7 */
+ /* 0:0 1-bit (not used) */
+};
+
+/* Decoded Card Specific Data (CSD) register */
+
+struct mmcsd_csd_s
+{
+ uint8_t csdstructure; /* 127:126 CSD structure */
+ uint8_t mmcspecvers; /* 125:122 MMC Spec version (MMC only) */
+
+ struct
+ {
+ uint8_t timeunit; /* 2:0 Time exponent */
+ uint8_t timevalue; /* 6:3 Time mantissa */
+ } taac; /* 119:112 Data read access-time-1 */
+
+ uint8_t nsac; /* 111:104 Data read access-time-2 in CLK cycle(NSAC*100) */
+
+ struct
+ {
+ uint8_t transferrateunit; /* 2:0 Rate exponent */
+ uint8_t timevalue; /* 6:3 Rate mantissa */
+ } transpeed; /* 103:96 Max. data transfer rate */
+
+ uint16_t ccc; /* 95:84 Card command classes */
+ uint8_t readbllen; /* 83:80 Max. read data block length */
+ uint8_t readblpartial; /* 79:79 Partial blocks for read allowed */
+ uint8_t writeblkmisalign; /* 78:78 Write block misalignment */
+ uint8_t readblkmisalign; /* 77:77 Read block misalignment */
+ uint8_t dsrimp; /* 76:76 DSR implemented */
+
+ union
+ {
+#ifdef CONFIG_MMCSD_MMCSUPPORT
+ struct
+ {
+ uint16_t csize; /* 73:62 Device size */
+ uint8_t vddrcurrmin; /* 61:59 Max. read current at Vdd min */
+ uint8_t vddrcurrmax; /* 58:56 Max. read current at Vdd max */
+ uint8_t vddwcurrmin; /* 55:53 Max. write current at Vdd min */
+ uint8_t vddwcurrmax; /* 52:50 Max. write current at Vdd max */
+ uint8_t csizemult; /* 49:47 Device size multiplier */
+
+ union
+ {
+ struct /* MMC system specification version 3.1 */
+ {
+ uint8_t ergrpsize; /* 46:42 Erase group size (MMC 3.1) */
+ uint8_t ergrpmult; /* 41:37 Erase group multiplier (MMC 3.1) */
+ } mmc31;
+ struct /* MMC system specification version 2.2 */
+ {
+ uint8_t sectorsize; /* 46:42 Erase sector size (MMC 2.2) */
+ uint8_t ergrpsize; /* 41:37 Erase group size (MMC 2.2) */
+ } mmc22;
+ } er;
+
+ uint8_t mmcwpgrpsize; /* 36:32 Write protect group size (MMC) */
+ } mmc;
+#endif
+ struct
+ {
+ uint16_t csize; /* 73:62 Device size */
+ uint8_t vddrcurrmin; /* 61:59 Max. read current at Vdd min */
+ uint8_t vddrcurrmax; /* 58:56 Max. read current at Vdd max */
+ uint8_t vddwcurrmin; /* 55:53 Max. write current at Vdd min */
+ uint8_t vddwcurrmax; /* 52:50 Max. write current at Vdd max */
+ uint8_t csizemult; /* 49:47 Device size multiplier */
+ uint8_t sderblen; /* 46:46 Erase single block enable (SD) */
+ uint8_t sdsectorsize; /* 45:39 Erase sector size (SD) */
+ uint8_t sdwpgrpsize; /* 38:32 Write protect group size (SD) */
+ } sdbyte;
+
+ struct
+ {
+ /* 73:70 (reserved) */
+ uint32_t csize; /* 69:48 Device size */
+ /* 47:47 (reserved) */
+ uint8_t sderblen; /* 46:46 Erase single block enable (SD) */
+ uint8_t sdsectorsize; /* 45:39 Erase sector size (SD) */
+ uint8_t sdwpgrpsize; /* 38:32 Write protect group size (SD) */
+ } sdblock;
+ } u;
+
+ uint8_t wpgrpen; /* 31:31 Write protect group enable */
+ uint8_t mmcdfltecc; /* 30:29 Manufacturer default ECC (MMC) */
+ uint8_t r2wfactor; /* 28:26 Write speed factor */
+ uint8_t writebllen; /* 25:22 Max. write data block length */
+ uint8_t writeblpartial; /* 21:21 Partial blocks for write allowed */
+ uint8_t fileformatgrp; /* 15:15 File format group */
+ uint8_t copy; /* 14:14 Copy flag (OTP) */
+ uint8_t permwriteprotect; /* 13:13 Permanent write protection */
+ uint8_t tmpwriteprotect; /* 12:12 Temporary write protection */
+ uint8_t fileformat; /* 10:11 File format */
+ uint8_t mmcecc; /* 9:8 ECC (MMC) */
+ uint8_t crc; /* 7:1 CRC */
+ /* 0:0 Not used */
+};
+
+struct mmcsd_scr_s
+{
+ uint8_t scrversion; /* 63:60 Version of SCR structure */
+ uint8_t sdversion; /* 59:56 SD memory card physical layer version */
+ uint8_t erasestate; /* 55:55 Data state after erase (1 or 0) */
+ uint8_t security; /* 54:52 SD security support */
+ uint8_t buswidth; /* 51:48 DAT bus widthes supported */
+ /* 47:32 SD reserved space */
+ uint32_t mfgdata; /* 31:0 Reserved for manufacturing data */
+};
+
+/********************************************************************************************
+ * Public Data
+ ********************************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/********************************************************************************************
+ * Public Functions
+ ********************************************************************************************/
+
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+#endif /* __DRIVERS_MMCSD_MMCSD_SDIO_H */
diff --git a/nuttx/drivers/mmcsd/mmcsd_spi.c b/nuttx/drivers/mmcsd/mmcsd_spi.c
new file mode 100644
index 000000000..7dbadc55f
--- /dev/null
+++ b/nuttx/drivers/mmcsd/mmcsd_spi.c
@@ -0,0 +1,1878 @@
+/****************************************************************************
+ * drivers/mmcsd/mmcsd_spi.c
+ *
+ * Copyright (C) 2008-2010, 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/clock.h>
+#include <nuttx/spi.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/mmcsd.h>
+
+#include "mmcsd_spi.h"
+#include "mmcsd_csd.h"
+#include "mmcsd_internal.h"
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_MMCSD_NSLOTS
+# ifdef CONFIG_CPP_HAVE_WARNING
+# warning "CONFIG_MMCSD_NSLOTS not defined"
+# endif
+# define CONFIG_MMCSD_NSLOTS 1
+#endif
+
+#define MMCSD_IDMODE_CLOCK (400000)
+
+#if defined(CONFIG_FS_WRITABLE) && !defined(CONFIG_MMCSD_READONLY)
+# define MMCSD_MODE 0666
+#else
+# define MMCSD_MODE 0444
+#endif
+
+#ifndef CONFIG_MMCSD_SPICLOCK
+# define CONFIG_MMCSD_SPICLOCK 20000000
+#endif
+
+#ifndef CONFIG_MMCSD_SECTOR512
+# define CONFIG_MMCSD_SECTOR512 /* Force 512 byte sectors on all cards */
+#endif
+
+/* Slot struct info *********************************************************/
+/* Slot status definitions */
+
+#define MMCSD_SLOTSTATUS_NOTREADY 0x01 /* Card not initialized */
+#define MMCSD_SLOTSTATUS_NODISK 0x02 /* No card in the slot */
+#define MMCSD_SLOTSTATUS_WRPROTECT 0x04 /* Card is write protected */
+#define MMCSD_SLOTSTATUS_MEDIACHGD 0x08 /* Media changed in slot */
+
+/* Values in the MMC/SD command table ***************************************/
+/* These define the value returned by the MMC/SD command */
+
+#define MMCSD_CMDRESP_R1 0
+#define MMCSD_CMDRESP_R1B 1
+#define MMCSD_CMDRESP_R2 2
+#define MMCSD_CMDRESP_R3 3
+#define MMCSD_CMDRESP_R7 4
+
+#ifdef CONFIG_MMCSD_SECTOR512
+# define SECTORSIZE(s) (512)
+#else
+# define SECTORSIZE(s) ((s)->sectorsize)
+#endif
+
+/* Time delays in units of the system clock. CLK_TCK is the number of clock
+ * ticks per second.
+ */
+
+#define MMCSD_DELAY_10MS (CLK_TCK/100 + 1)
+#define MMCSD_DELAY_50MS (CLK_TCK/20 + 1)
+#define MMCSD_DELAY_100MS (CLK_TCK/10 + 1)
+#define MMCSD_DELAY_250MS (CLK_TCK/4 + 1)
+#define MMCSD_DELAY_500MS (CLK_TCK/2 + 1)
+#define MMCSD_DELAY_1SEC (CLK_TCK + 1)
+#define MMCSD_DELAY_10SEC (10 * CLK_TCK + 1)
+
+#define ELAPSED_TIME(t) (clock_systimer()-(t))
+#define START_TIME (clock_systimer())
+
+/* SD read timeout: ~100msec, Write Time out ~250ms. Units of clock ticks */
+
+#define SD_READACCESS MMCSD_DELAY_100MS
+#define SD_WRITEACCESS MMCSD_DELAY_250MS
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure represents the state of one card slot */
+
+struct mmcsd_slot_s
+{
+ FAR struct spi_dev_s *spi; /* SPI port bound to this slot */
+ sem_t sem; /* Assures mutually exclusive accesss to card and SPI */
+ uint8_t state; /* State of the slot (see MMCSD_SLOTSTATUS_* definitions) */
+ uint8_t type; /* Disk type */
+ uint8_t csd[16]; /* Copy of card CSD */
+#ifndef CONFIG_MMCSD_SECTOR512
+ uint16_t sectorsize; /* Media block size (in bytes) */
+#endif
+ uint32_t nsectors; /* Number of blocks on the media */
+ uint32_t taccess; /* Card access time */
+ uint32_t twrite; /* Card write time */
+ uint32_t ocr; /* Last 4 bytes of OCR (R3) */
+ uint32_t r7; /* Last 4 bytes of R7 */
+};
+
+struct mmcsd_cmdinfo_s
+{
+ uint8_t cmd;
+ uint8_t resp;
+ uint8_t chksum;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Misc *********************************************************************/
+
+static void mmcsd_semtake(sem_t *sem);
+
+/* Card SPI interface *******************************************************/
+
+static int mmcsd_waitready(FAR struct mmcsd_slot_s *slot);
+static uint32_t mmcsd_sendcmd(FAR struct mmcsd_slot_s *slot,
+ const struct mmcsd_cmdinfo_s *cmd, uint32_t arg);
+static void mmcsd_setblklen(FAR struct mmcsd_slot_s *slot,
+ uint32_t length);
+static uint32_t mmcsd_nsac(FAR struct mmcsd_slot_s *slot, uint8_t *csd,
+ uint32_t frequency);
+static uint32_t mmcsd_taac(FAR struct mmcsd_slot_s *slot, uint8_t *csd);
+static void mmcsd_decodecsd(FAR struct mmcsd_slot_s *slot, uint8_t *csd);
+static void mmcsd_checkwrprotect(FAR struct mmcsd_slot_s *slot,
+ uint8_t *csd);
+static int mmcsd_getcardinfo(FAR struct mmcsd_slot_s *slot,
+ uint8_t *buffer, const struct mmcsd_cmdinfo_s *cmd);
+
+#define mmcsd_getcsd(slot, csd) mmcsd_getcardinfo(slot, csd, &g_cmd9);
+#define mmcsd_getcid(slot, cid) mmcsd_getcardinfo(slot, cid, &g_cmd10);
+
+static int mmcsd_recvblock(FAR struct mmcsd_slot_s *slot,
+ uint8_t *buffer, int nbytes);
+#if defined(CONFIG_FS_WRITABLE) && !defined(CONFIG_MMCSD_READONLY)
+static int mmcsd_xmitblock(FAR struct mmcsd_slot_s *slot,
+ const uint8_t *buffer, int nbytes, uint8_t token);
+#endif
+
+/* Block driver interfaces **************************************************/
+
+static int mmcsd_open(FAR struct inode *inode);
+static int mmcsd_close(FAR struct inode *inode);
+static ssize_t mmcsd_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors);
+#if defined(CONFIG_FS_WRITABLE) && !defined(CONFIG_MMCSD_READONLY)
+static ssize_t mmcsd_write(FAR struct inode *inode,
+ const unsigned char *buffer, size_t start_sector,
+ unsigned int nsectors);
+#endif
+static int mmcsd_geometry(FAR struct inode *inode,
+ struct geometry *geometry);
+
+/* Initialization ***********************************************************/
+
+static int mmcsd_mediainitialize(FAR struct mmcsd_slot_s *slot);
+static void mmcsd_mediachanged(void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Driver state *************************************************************/
+
+/* These are the lock driver methods supported by this file */
+
+static const struct block_operations g_bops =
+{
+ mmcsd_open, /* open */
+ mmcsd_close, /* close */
+ mmcsd_read, /* read */
+#if defined(CONFIG_FS_WRITABLE) && !defined(CONFIG_MMCSD_READONLY)
+ mmcsd_write, /* write */
+#else
+ NULL, /* write */
+#endif
+ mmcsd_geometry, /* geometry */
+ NULL /* ioctl */
+};
+
+/* A slot structure allocated for each configured slot */
+
+static struct mmcsd_slot_s g_mmcsdslot[CONFIG_MMCSD_NSLOTS];
+
+/* Timing *******************************************************************/
+
+/* We will use the TRAN_SPEED from the CSD to determine the maximum SPI
+ * clocking (TRAN_SPEED defines the maximum transfer rate per bit per data
+ * line).
+ *
+ * The CSD TRAN_SPEED is provided as a 3 bit rate unit (RU) and a 4 bit time
+ * value (TU). We need the transfer frequency which is: RU*TU bits/sec
+ *
+ * g_transpeedru holds RU/10 and g_transpeedtu holds TU*10 so that the
+ * correct value is returned in the product
+ */
+
+static const uint32_t g_transpeedru[8] =
+{
+ 10000, /* 0: 10 Kbit/sec / 10 */
+ 100000, /* 1: 1 Mbit/sec / 10 */
+ 1000000, /* 2: 10 Mbit/sec / 10 */
+ 10000000, /* 3: 100 Mbit/sec / 10*/
+
+ 0, 0, 0, 0 /* 4-7: Reserved values */
+};
+
+static const uint32_t g_transpeedtu[16] =
+{
+ 0, 10, 12, 13, /* 0-3: Reserved, 1.0, 1.1, 1.2, 1.3 */
+ 15, 20, 25, 30, /* 4-7: 1.5, 2.0, 2.5, 3.0 */
+ 35, 40, 45, 50, /* 8-11: 3.5, 4.0, 4.5, 5.0 */
+ 55, 60, 70, 80, /* 12-15: 5.5, 6.0, 7.0, 8.0 */
+};
+
+/* The TAAC defines the asynchronous part of the data access time. The
+ * read access time the sum of the TAAC and the NSAC. These define the
+ * time from the end bit of the read command to start bit of the data block.
+ *
+ * The TAAC consists of a 3-bit time unit (TU) and a 4-bit time value (TV).
+ * TAAC is in units of time; NSAC is in units of SPI clocks.
+ * The access time we need is then given by:
+ *
+ * taccess = TU*TV + NSAC/spifrequency
+ *
+ * g_taactu holds TU in units of nanoseconds and microseconds (you have to use
+ * the index to distiguish). g_taactv holds TV with 8-bits of fraction.
+ */
+
+#define MAX_USTUNDX 2
+static const uint16_t g_taactu[8] =
+{
+ /* Units of nanoseconds */
+
+ 1, /* 0: 1 ns */
+ 10, /* 1: 10 ns */
+ 100, /* 2: 100 ns */
+
+ /* Units of microseconds */
+
+ 1, /* 3: 1 us 1,000 ns */
+ 10, /* 4: 10 us 10,000 ns */
+ 100, /* 5: 100 us 100,000 ns */
+ 1000, /* 6: 1 ms 1,000,000 ns*/
+ 10000, /* 7: 10 ms 10,000,000 ns */
+};
+
+static const uint16_t g_taactv[] =
+{
+ 0x000, 0x100, 0x133, 0x14d, /* 0-3: Reserved, 1.0, 1.2, 1.3 */
+ 0x180, 0x200, 0x280, 0x300, /* 4-7: 1.5, 2.0, 2.5, 3.0 */
+ 0x380, 0x400, 0x480, 0x500, /* 8-11: 3.5, 4.0, 4.5, 5.0 */
+ 0x580, 0x600, 0x700, 0x800 /* 12-15: 5.5, 6.0, 7.0, 8.0 */
+};
+
+/* Commands *****************************************************************/
+
+static const struct mmcsd_cmdinfo_s g_cmd0 = {CMD0, MMCSD_CMDRESP_R1, 0x95};
+static const struct mmcsd_cmdinfo_s g_cmd1 = {CMD1, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_cmd8 = {CMD8, MMCSD_CMDRESP_R7, 0x87};
+static const struct mmcsd_cmdinfo_s g_cmd9 = {CMD9, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_cmd10 = {CMD10, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_cmd12 = {CMD12, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_cmd16 = {CMD16, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_cmd17 = {CMD17, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_cmd18 = {CMD18, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_cmd24 = {CMD24, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_cmd25 = {CMD25, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_cmd55 = {CMD55, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_cmd58 = {CMD58, MMCSD_CMDRESP_R3, 0xff};
+static const struct mmcsd_cmdinfo_s g_acmd23 = {ACMD23, MMCSD_CMDRESP_R1, 0xff};
+static const struct mmcsd_cmdinfo_s g_acmd41 = {ACMD41, MMCSD_CMDRESP_R1, 0xff};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mmcsd_semtake
+ ****************************************************************************/
+
+static void mmcsd_semtake(sem_t *sem)
+{
+ while (sem_wait(sem) != 0)
+ {
+ /* The only case that an error should occur here is if the wait was
+ * awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+#define mmcsd_semgive(sem) sem_post(sem)
+
+/****************************************************************************
+ * Name: mmcsd_waitready
+ *
+ * Description:
+ * Wait until the card is no longer busy
+ *
+ * Assumptions:
+ * MMC/SD card already selected
+ *
+ ****************************************************************************/
+
+static int mmcsd_waitready(FAR struct mmcsd_slot_s *slot)
+{
+ FAR struct spi_dev_s *spi = slot->spi;
+ uint8_t response;
+ uint32_t start;
+ uint32_t elapsed;
+
+ /* Wait until the card is no longer busy (up to 500MS) */
+
+ start = START_TIME;
+ do
+ {
+ response = SPI_SEND(spi, 0xff);
+ if (response == 0xff)
+ {
+ return OK;
+ }
+ elapsed = ELAPSED_TIME(start);
+ }
+ while (elapsed < MMCSD_DELAY_500MS);
+
+ fdbg("Card still busy, last response: %02x\n", response);
+ return -EBUSY;
+}
+
+/****************************************************************************
+ * Name: mmcsd_sendcmd
+ *
+ * Description:
+ * Send a command to MMC
+ *
+ * Assumptions:
+ * MMC/SD card already selected
+ *
+ ****************************************************************************/
+
+static uint32_t mmcsd_sendcmd(FAR struct mmcsd_slot_s *slot,
+ const struct mmcsd_cmdinfo_s *cmd, uint32_t arg)
+{
+ FAR struct spi_dev_s *spi = slot->spi;
+ uint32_t result;
+ uint8_t response = 0xff;
+ int ret;
+ int i;
+
+ /* Wait until the card is not busy */
+
+ ret = mmcsd_waitready(slot);
+ if (ret != OK)
+ {
+ return ret;
+ }
+
+ /* Send command code */
+
+ SPI_SEND(spi, cmd->cmd);
+
+ /* Send command's arguments (should be zero if there are no arguements) */
+
+ SPI_SEND(spi, (arg >> 24) & 0xff);
+ SPI_SEND(spi, (arg >> 16) & 0xff);
+ SPI_SEND(spi, (arg >> 8) & 0xff);
+ SPI_SEND(spi, arg & 0xff);
+
+ /* Send CRC if needed. The SPI interface is initialized in non-protected
+ * mode. However, the reset command (CMD0) and CMD8 are received by the
+ * card while it is still in SD mode and, therefore, must have a valid
+ * CRC field.
+ */
+
+ SPI_SEND(spi, cmd->chksum);
+
+ /* Skip stuff byte on CMD12 */
+
+ if (cmd->cmd == CMD12)
+ {
+ SPI_SEND(spi, 0xff);
+ }
+
+ /* Get the response to the command. A valid response will have bit7=0.
+ * Usually, the non-response is 0xff, but I have seen 0xc0 too.
+ */
+
+ for (i = 0; i < 9 && (response & 0x80) != 0; i++)
+ {
+ response = SPI_SEND(spi, 0xff);
+ }
+
+ if ((response & 0x80) != 0)
+ {
+ fdbg("Failed: i=%d response=%02x\n", i, response);
+ return (uint32_t)-1;
+ }
+
+ /* Interpret the response according to the command */
+
+ result = response;
+ switch (cmd->resp)
+ {
+ /* The R1B response is two bytes long */
+
+ case MMCSD_CMDRESP_R1B:
+ {
+ uint32_t busy = 0;
+ uint32_t start;
+ uint32_t elapsed;
+
+ start = START_TIME;
+ do
+ {
+ busy = SPI_SEND(spi, 0xff);
+ elapsed = ELAPSED_TIME(start);
+ }
+ while (elapsed < slot->twrite && busy != 0xff);
+
+ if (busy != 0xff)
+ {
+ fdbg("Failed: card still busy (%02x)\n", busy);
+ return (uint32_t)-1;
+ }
+
+ fvdbg("CMD%d[%08x] R1B=%02x\n",
+ cmd->cmd & 0x3f, arg, response);
+ }
+ break;
+
+ /* The R1 response is a single byte */
+
+ case MMCSD_CMDRESP_R1:
+ {
+ fvdbg("CMD%d[%08x] R1=%02x\n",
+ cmd->cmd & 0x3f, arg, response);
+ }
+ break;
+
+ /* The R2 response is two bytes long */
+
+ case MMCSD_CMDRESP_R2:
+ {
+ result = ((uint32_t)(response & 0xff) << 8);
+ result |= SPI_SEND(spi, 0xff) & 0xff;
+
+ fvdbg("CMD%d[%08x] R2=%04x\n",
+ cmd->cmd & 0x3f, arg, result);
+ }
+ break;
+
+ /* The R3 response is 5 bytes long */
+
+ case MMCSD_CMDRESP_R3:
+ {
+ slot->ocr = ((uint32_t)(SPI_SEND(spi, 0xff) & 0xff) << 24);
+ slot->ocr |= ((uint32_t)(SPI_SEND(spi, 0xff) & 0xff) << 16);
+ slot->ocr |= ((uint32_t)(SPI_SEND(spi, 0xff) & 0xff) << 8);
+ slot->ocr |= SPI_SEND(spi, 0xff) & 0xff;
+
+ fvdbg("CMD%d[%08x] R1=%02x OCR=%08x\n",
+ cmd->cmd & 0x3f, arg, response, slot->ocr);
+ }
+
+ /* The R7 response is 5 bytes long */
+ case MMCSD_CMDRESP_R7:
+ default:
+ {
+ slot->r7 = ((uint32_t)(SPI_SEND(spi, 0xff) & 0xff) << 24);
+ slot->r7 |= ((uint32_t)(SPI_SEND(spi, 0xff) & 0xff) << 16);
+ slot->r7 |= ((uint32_t)(SPI_SEND(spi, 0xff) & 0xff) << 8);
+ slot->r7 |= SPI_SEND(spi, 0xff) & 0xff;
+
+ fvdbg("CMD%d[%08x] R1=%02x R7=%08x\n",
+ cmd->cmd & 0x3f, arg, response, slot->r7);
+ }
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ * Name: mmcsd_setblklen
+ *
+ * Description:
+ * Set block length
+ *
+ * Assumptions:
+ * MMC/SD card already selected
+ *
+ ****************************************************************************/
+
+static void mmcsd_setblklen(FAR struct mmcsd_slot_s *slot, uint32_t length)
+{
+ uint32_t response;
+
+ fvdbg("Set block length to %d\n", length);
+ response = mmcsd_sendcmd(slot, &g_cmd16, length);
+ if (response != MMCSD_SPIR1_OK)
+ {
+ fdbg("Failed to set block length: %02x\n", response);
+ }
+}
+
+/****************************************************************************
+ * Name: mmcsd_nsac
+ *
+ * Description: Convert the value of the NSAC to microseconds
+ *
+ ****************************************************************************/
+
+static uint32_t mmcsd_nsac(FAR struct mmcsd_slot_s *slot, uint8_t *csd,
+ uint32_t frequency)
+{
+ /* NSAC is 8-bits wide and is in units of 100 clock cycles. Therefore, the
+ * maximum value is 25.5K clock cycles.
+ */
+
+ uint32_t nsac = MMCSD_CSD_NSAC(csd) * ((uint32_t)100*1000);
+ uint32_t fhkz = (frequency + 500) / 1000;
+ return (nsac + (fhkz >> 1)) / fhkz;
+}
+
+/****************************************************************************
+ * Name: mmcsd_taac
+ *
+ * Description: Convert the value of the TAAC to microseconds
+ *
+ ****************************************************************************/
+
+static uint32_t mmcsd_taac(FAR struct mmcsd_slot_s *slot, uint8_t *csd)
+{
+ int tundx;
+
+ /*The TAAC consists of a 3-bit time unit (TU) and a 4-bit time value (TV).
+ * TAAC is in units of time; NSAC is in units of SPI clocks.
+ * The access time we need is then given by:
+ *
+ * taccess = TU*TV + NSAC/spifrequency
+ *
+ * g_taactu holds TU in units of nanoseconds and microseconds (you have to use
+ * the index to distiguish. g_taactv holds TV with 8-bits of fraction.
+ */
+
+ tundx = MMCSD_CSD_TAAC_TIMEUNIT(csd);
+ if (tundx <= MAX_USTUNDX)
+ {
+ /* The maximum value of the nanosecond TAAC is 800 ns. The rounded
+ * answer in microseconds will be at most 1.
+ */
+
+ return 1;
+ }
+ else
+ {
+ /* Return the answer in microseconds */
+
+ return (g_taactu[tundx]*g_taactv[MMCSD_CSD_TAAC_TIMEVALUE(csd)] + 0x80) >> 8;
+ }
+}
+
+/****************************************************************************
+ * Name: mmcsd_decodecsd
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static void mmcsd_decodecsd(FAR struct mmcsd_slot_s *slot, uint8_t *csd)
+{
+ FAR struct spi_dev_s *spi = slot->spi;
+ uint32_t maxfrequency;
+ uint32_t frequency;
+ uint32_t readbllen;
+ uint32_t csizemult;
+ uint32_t csize;
+
+ /* Calculate SPI max clock */
+
+ maxfrequency =
+ g_transpeedtu[MMCSD_CSD_TRANSPEED_TIMEVALUE(csd)] *
+ g_transpeedru[MMCSD_CSD_TRANSPEED_TRANSFERRATEUNIT(csd)];
+
+ /* Clip the max frequency to account for board limitations */
+
+ frequency = maxfrequency;
+ if (frequency > CONFIG_MMCSD_SPICLOCK)
+ {
+ frequency = CONFIG_MMCSD_SPICLOCK;
+ }
+
+ /* Set the actual SPI frequency as close as possible to that value */
+
+ frequency = SPI_SETFREQUENCY(spi, frequency);
+
+ /* Now determine the delay to access data */
+
+ if (slot->type == MMCSD_CARDTYPE_MMC)
+ {
+ /* The TAAC consists of a 3-bit time unit (TU) and a 4-bit time value (TV).
+ * TAAC is in units of time; NSAC is in units of SPI clocks.
+ * The access time we need is then given by:
+ *
+ * taccess = TU*TV + NSAC/spifrequency
+ *
+ * Example: TAAC = 1.5 ms, NSAC = 0, r2wfactor = 4, CLK_TCK=100
+ * taccessus = 1,500uS
+ * taccess = (1,500 * 100) / 100,000) + 1 = 2 (ideal, 1.5)
+ * twrite = (1,500 * 4 * 100) / 100,000) + 1 = 7 (ideal 6.0)
+ *
+ * First get the access time in microseconds
+ */
+
+ uint32_t taccessus = mmcsd_taac(slot, csd) + mmcsd_nsac(slot, csd, frequency);
+
+ /* Then convert to system clock ticks. The maximum read access is 10 times
+ * the tacc value: taccess = 10 * (taccessus / 1,000,000) * CLK_TCK, or
+ */
+
+ slot->taccess = (taccessus * CLK_TCK) / 100000 + 1;
+
+ /* NOTE that we add one to taccess to assure that we wait at least this
+ * time. The write access time is larger by the R2WFACTOR: */
+
+ slot->taccess = (taccessus * MMCSD_CSD_R2WFACTOR(csd) * CLK_TCK) / 100000 + 1;
+ }
+ else
+ {
+ /* For SD, the average is still given by the TAAC+NSAC, but the
+ * maximum are the constants 100 and 250MS
+ */
+
+ slot->taccess = SD_READACCESS;
+ slot->twrite = SD_WRITEACCESS;
+ }
+
+ fvdbg("SPI Frequency\n");
+ fvdbg(" Maximum: %d Hz\n", maxfrequency);
+ fvdbg(" Actual: %d Hz\n", frequency);
+ fvdbg("Read access time: %d ticks\n", slot->taccess);
+ fvdbg("Write access time: %d ticks\n", slot->twrite);
+
+ /* Get the physical geometry of the card: sector size and number of
+ * sectors. The card's total capacity is computed from
+ *
+ * capacity = BLOCKNR * BLOCK_LEN
+ * BLOCKNR = (C_SIZE+1)*MULT
+ * MULT = 2**(C_SIZE_MULT+2) (C_SIZE_MULT < 8)
+ * BLOCK_LEN = 2**READ_BL_LEN (READ_BL_LEN < 12)
+ *
+ * Or
+ *
+ * capacity = ((C_SIZE+1) << (READD_BL_LEN + C_SIZE_MULT + 2))
+ *
+ * In units of the sector size (1 << READ_BL_LEN), then simplifies to
+ *
+ * nsectors = ((C_SIZE+1) << (C_SIZE_MULT + 2))
+ */
+
+ if (MMCSD_CSD_CSDSTRUCT(csd) != 0)
+ {
+ /* SDC structure ver 2.xx */
+ /* Note: On SD card WRITE_BL_LEN is always the same as READ_BL_LEN */
+
+ readbllen = SD20_CSD_READBLLEN(csd);
+ csizemult = SD20_CSD_CSIZEMULT(csd) + 2;
+ csize = SD20_CSD_CSIZE(csd) + 1;
+ }
+ else
+ {
+ /* MMC or SD structure ver 1.xx */
+ /* Note: On SD card WRITE_BL_LEN is always the same as READ_BL_LEN */
+
+ readbllen = MMCSD_CSD_READBLLEN(csd);
+ csizemult = MMCSD_CSD_CSIZEMULT(csd) + 2;
+ csize = MMCSD_CSD_CSIZE(csd) + 1;
+ }
+
+ /* SDHC ver2.x cards have fixed block transfer size of 512 bytes. SDC
+ * ver1.x cards with capacity less than 1Gb, will have sector size
+ * 512 byes. SDC ver1.x cards with capacity of 2Gb will report readbllen
+ * of 1024 but should use 512 bytes for block transfers. SDC ver1.x 4Gb
+ * cards will report readbllen of 2048 bytes -- are they also 512 bytes?
+ */
+
+#ifdef CONFIG_MMCSD_SECTOR512
+ if (readbllen > 9)
+ {
+ csizemult += (readbllen - 9);
+ }
+ else
+ {
+ DEBUGASSERT(readbllen == 9);
+ }
+#else
+ if (IS_SDV2(slot->type))
+ {
+ if (readbllen > 9)
+ {
+ fdbg("Forcing 512 byte sector size\n");
+ csizemult += (readbllen - 9);
+ readbllen = 9;
+ }
+ }
+
+ slot->sectorsize = 1 << readbllen;
+#endif
+ slot->nsectors = csize << csizemult;
+ fvdbg("Sector size: %d\n", SECTORSIZE(slot));
+ fvdbg("Number of sectors: %d\n", slot->nsectors);
+}
+
+/****************************************************************************
+ * Name: mmcsd_checkwrprotect
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static void mmcsd_checkwrprotect(FAR struct mmcsd_slot_s *slot, uint8_t *csd)
+{
+ FAR struct spi_dev_s *spi = slot->spi;
+
+ /* Check if (1) the slot is reporting that reporting that write protection
+ * is set, (2) the card reports permanent write protect, or (2) the card
+ * reports temporary write protect.
+ */
+
+ if ((SPI_STATUS(spi, SPIDEV_MMCSD) & SPI_STATUS_WRPROTECTED) != 0 ||
+ MMCSD_CSD_PERMWRITEPROTECT(csd) ||
+ MMCSD_CSD_TMPWRITEPROTECT(csd))
+ {
+ slot->state |= MMCSD_SLOTSTATUS_WRPROTECT;
+ }
+ else
+ {
+ slot->state &= ~MMCSD_SLOTSTATUS_WRPROTECT;
+ }
+}
+
+/****************************************************************************
+ * Name: mmcsd_getcardinfo
+ *
+ * Description:
+ * Read CSD or CID registers
+ *
+ * Assumptions:
+ * MMC/SD card already selected
+ *
+ ****************************************************************************/
+
+static int mmcsd_getcardinfo(FAR struct mmcsd_slot_s *slot, uint8_t *buffer,
+ const struct mmcsd_cmdinfo_s *cmd)
+{
+ FAR struct spi_dev_s *spi = slot->spi;
+ uint32_t result;
+ uint8_t response;
+ int i;
+
+ SPI_SEND(spi, 0xff);
+
+ /* Send the CMD9 or CMD10 */
+
+ result = mmcsd_sendcmd(slot, cmd, 0);
+ if (result != MMCSD_SPIR1_OK)
+ {
+ fdbg("CMD9/10 failed: R1=%02x\n", result);
+ return -EIO;
+ }
+
+ /* Try up to 8 times to find the start of block (or until an error occurs) */
+
+ for (i = 0; i < 8; i++)
+ {
+ response = SPI_SEND(spi, 0xff);
+ fvdbg("%d. SPI send returned %02x\n", i, response);
+
+ /* If a read operation fails and the card cannot provide the requested
+ * data, it will send a data error token instead. The 4 least
+ * significant bits are the same as those in the R2 response.
+ */
+
+ if (response != 0 && (response & MMCSD_SPIDET_UPPER) == 0)
+ {
+ fdbg("%d. Data transfer error: %02x\n", i, response);
+ return -EIO;
+ }
+ else if (response == MMCSD_SPIDT_STARTBLKSNGL)
+ {
+ for (i = 0; i < 16; ++i)
+ {
+ *buffer++ = SPI_SEND(spi, 0xff);
+ }
+
+ /* CRC receive */
+
+ SPI_SEND(spi, 0xff);
+ SPI_SEND(spi, 0xff);
+ return OK;
+ }
+ }
+
+ fdbg("%d. Did not find start of block\n");
+ return -EIO;
+}
+
+/****************************************************************************
+ * Name: mmcsd_recvblock
+ *
+ * Description: Receive a data block from the card
+ *
+ ****************************************************************************/
+
+static int mmcsd_recvblock(FAR struct mmcsd_slot_s *slot, uint8_t *buffer, int nbytes)
+{
+ FAR struct spi_dev_s *spi = slot->spi;
+ uint32_t start;
+ uint32_t elapsed;
+ uint8_t token;
+
+ /* Wait up to the maximum to receive a valid data token. taccess is the
+ * time from when the command is sent until the first byte of data is
+ * received */
+
+ start = START_TIME;
+ do
+ {
+ token = SPI_SEND(spi, 0xff);
+ elapsed = ELAPSED_TIME(start);
+ }
+ while (token == 0xff && elapsed < slot->taccess);
+
+ if (token == MMCSD_SPIDT_STARTBLKSNGL)
+ {
+ /* Receive the block */
+
+ SPI_RECVBLOCK(spi, buffer, nbytes);
+
+ /* Discard the CRC */
+
+ SPI_SEND(spi, 0xff);
+ SPI_SEND(spi, 0xff);
+ return OK;
+ }
+
+ fdbg("Did not receive data token (%02x)\n", token);
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: mmcsd_xmitblock
+ *
+ * Description: Transmit a data block to the card
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_FS_WRITABLE) && !defined(CONFIG_MMCSD_READONLY)
+static int mmcsd_xmitblock(FAR struct mmcsd_slot_s *slot, const uint8_t *buffer,
+ int nbytes, uint8_t token)
+{
+ FAR struct spi_dev_s *spi = slot->spi;
+ uint8_t response;
+
+ /* Start the block transfer:
+ * 1. 0xff (sync)
+ * 2. 0xfe or 0xfc (start of block token)
+ * 3. Followed by the block of data and 2 byte CRC
+ */
+
+ SPI_SEND(spi, 0xff); /* sync */
+ SPI_SEND(spi, token); /* data token */
+
+ /* Transmit the block to the MMC/SD card */
+
+ (void)SPI_SNDBLOCK(spi, buffer, nbytes);
+
+ /* Add the bogus CRC. By default, the SPI interface is initialized in
+ * non-protected mode. However, we still have to send bogus CRC values
+ */
+
+ SPI_SEND(spi, 0xff);
+ SPI_SEND(spi, 0xff);
+
+ /* Now get the data response */
+
+ response = SPI_SEND(spi, 0xff);
+ if ((response & MMCSD_SPIDR_MASK) != MMCSD_SPIDR_ACCEPTED)
+ {
+ fdbg("Bad data response: %02x\n", response);
+ return -EIO;
+ }
+ return OK;
+}
+#endif /* CONFIG_FS_WRITABLE && !CONFIG_MMCSD_READONLY */
+
+/****************************************************************************
+ * Block Driver Operations
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mmcsd_open
+ *
+ * Description: Open the block device
+ *
+ ****************************************************************************/
+
+static int mmcsd_open(FAR struct inode *inode)
+{
+ FAR struct mmcsd_slot_s *slot;
+ FAR struct spi_dev_s *spi;
+ int ret;
+
+ fvdbg("Entry\n");
+
+#ifdef CONFIG_DEBUG
+ if (!inode || !inode->i_private)
+ {
+ fdbg("Internal confusion\n");
+ return -EIO;
+ }
+#endif
+
+ /* Extract our private data from the inode structure */
+
+ slot = (FAR struct mmcsd_slot_s *)inode->i_private;
+ spi = slot->spi;
+
+#ifdef CONFIG_DEBUG
+ if (!spi)
+ {
+ fdbg("Internal confusion\n");
+ return -EIO;
+ }
+#endif
+
+ /* Verify that an MMC/SD card has been inserted */
+
+ ret = -ENODEV;
+ mmcsd_semtake(&slot->sem);
+ if ((SPI_STATUS(spi, SPIDEV_MMCSD) & SPI_STATUS_PRESENT) != 0)
+ {
+ /* Yes.. a card is present. Has it been initialized? */
+
+ if (slot->type == MMCSD_CARDTYPE_UNKNOWN)
+ {
+ /* Ininitialize for the media in the slot */
+
+ ret = mmcsd_mediainitialize(slot);
+ if (ret < 0)
+ {
+ fvdbg("Failed to initialize card\n");
+ goto errout_with_sem;
+ }
+ }
+
+ /* Make sure that the card is ready */
+
+ SPI_SELECT(spi, SPIDEV_MMCSD, true);
+ ret = mmcsd_waitready(slot);
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ }
+
+errout_with_sem:
+ mmcsd_semgive(&slot->sem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mmcsd_close
+ *
+ * Description: close the block device
+ *
+ ****************************************************************************/
+
+static int mmcsd_close(FAR struct inode *inode)
+{
+ fvdbg("Entry\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mmcsd_read
+ *
+ * Description: Read the specified numer of sectors
+ *
+ ****************************************************************************/
+
+static ssize_t mmcsd_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
+{
+ FAR struct mmcsd_slot_s *slot;
+ FAR struct spi_dev_s *spi;
+ size_t nbytes;
+ off_t offset;
+ uint8_t response;
+ int i;
+
+ fvdbg("start_sector=%d nsectors=%d\n", start_sector, nsectors);
+
+#ifdef CONFIG_DEBUG
+ if (!buffer)
+ {
+ fdbg("Invalid parameters\n");
+ return -EINVAL;
+ }
+
+ if (!inode || !inode->i_private)
+ {
+ fdbg("Internal confusion\n");
+ return -EIO;
+ }
+#endif
+
+ /* Extract our private data from the inode structure */
+
+ slot = (FAR struct mmcsd_slot_s *)inode->i_private;
+ spi = slot->spi;
+
+#ifdef CONFIG_DEBUG
+ if (!spi)
+ {
+ fdbg("Internal confusion\n");
+ return -EIO;
+ }
+#endif
+
+ /* Verify that card is available */
+
+ if (slot->state & MMCSD_SLOTSTATUS_NOTREADY)
+ {
+ fdbg("Slot not ready\n");
+ return -ENODEV;
+ }
+
+ /* Do nothing on zero-length transfer */
+
+ if (nsectors < 1)
+ {
+ return 0;
+ }
+
+ /* Convert sector and nsectors to nbytes and byte offset */
+
+ nbytes = nsectors * SECTORSIZE(slot);
+ if (IS_BLOCK(slot->type))
+ {
+ offset = start_sector;
+ fvdbg("nbytes=%d sector offset=%d\n", nbytes, offset);
+ }
+ else
+ {
+ offset = start_sector * SECTORSIZE(slot);
+ fvdbg("nbytes=%d byte offset=%d\n", nbytes, offset);
+ }
+
+ /* Select the slave */
+
+ mmcsd_semtake(&slot->sem);
+ SPI_SELECT(spi, SPIDEV_MMCSD, true);
+
+ /* Single or multiple block read? */
+
+ if (nsectors == 1)
+ {
+ /* Send CMD17: Reads a block of the size selected by the SET_BLOCKLEN
+ * command and verify that good R1 status is returned
+ */
+
+ response = mmcsd_sendcmd(slot, &g_cmd17, offset);
+ if (response != MMCSD_SPIR1_OK)
+ {
+ fdbg("CMD17 failed: R1=%02x\n", response);
+ goto errout_with_eio;
+ }
+
+ /* Receive the block */
+
+ if (mmcsd_recvblock(slot, buffer, SECTORSIZE(slot)) != 0)
+ {
+ fdbg("Failed: to receive the block\n");
+ goto errout_with_eio;
+ }
+ }
+ else
+ {
+ /* Send CMD18: Reads a block of the size selected by the SET_BLOCKLEN
+ * command and verify that good R1 status is returned
+ */
+
+ response = mmcsd_sendcmd(slot, &g_cmd18, offset);
+ if (response != MMCSD_SPIR1_OK)
+ {
+ fdbg("CMD118 failed: R1=%02x\n", response);
+ goto errout_with_eio;
+ }
+
+ /* Receive each block */
+
+ for (i = 0; i < nsectors; i++)
+ {
+ if (mmcsd_recvblock(slot, buffer, SECTORSIZE(slot)) != 0)
+ {
+ fdbg("Failed: to receive the block\n");
+ goto errout_with_eio;
+ }
+ buffer += SECTORSIZE(slot);
+ }
+
+ /* Send CMD12: Stops transmission */
+
+ response = mmcsd_sendcmd(slot, &g_cmd12, 0);
+ }
+
+ /* On success, return the number of sectors transfer */
+
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ SPI_SEND(spi, 0xff);
+ mmcsd_semgive(&slot->sem);
+
+ fvdbg("Read %d bytes:\n", nbytes);
+ mmcsd_dumpbuffer("Read buffer", buffer, nbytes);
+ return nsectors;
+
+errout_with_eio:
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ mmcsd_semgive(&slot->sem);
+ return -EIO;
+}
+
+/****************************************************************************
+ * Name: mmcsd_write
+ *
+ * Description:
+ * Write the specified number of sectors
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_FS_WRITABLE) && !defined(CONFIG_MMCSD_READONLY)
+static ssize_t mmcsd_write(FAR struct inode *inode, const unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
+{
+ FAR struct mmcsd_slot_s *slot;
+ FAR struct spi_dev_s *spi;
+ size_t nbytes;
+ off_t offset;
+ uint8_t response;
+ int ret;
+ int i;
+
+ fvdbg("start_sector=%d nsectors=%d\n", start_sector, nsectors);
+
+#ifdef CONFIG_DEBUG
+ if (!buffer)
+ {
+ fdbg("Invalid parameters\n");
+ return -EINVAL;
+ }
+
+ if (!inode || !inode->i_private)
+ {
+ fdbg("Internal confusion\n");
+ return -EIO;
+ }
+#endif
+
+ /* Extract our private data from the inode structure */
+
+ slot = (FAR struct mmcsd_slot_s *)inode->i_private;
+ spi = slot->spi;
+
+#ifdef CONFIG_DEBUG
+ if (!spi)
+ {
+ fdbg("Internal confusion\n");
+ return -EIO;
+ }
+#endif
+
+ /* Verify that card is available */
+
+ if (slot->state & MMCSD_SLOTSTATUS_NOTREADY)
+ {
+ fdbg("Slot not ready\n");
+ return -ENODEV;
+ }
+
+ /* Verify that the card is write enabled */
+
+ if (slot->state & MMCSD_SLOTSTATUS_WRPROTECT)
+ {
+ fdbg("Not write enabled\n");
+ return -EACCES;
+ }
+
+ /* Do nothing on zero-length transfer */
+
+ if (nsectors < 1)
+ {
+ return 0;
+ }
+
+ /* Convert sector and nsectors to nbytes and byte offset */
+
+ nbytes = nsectors * SECTORSIZE(slot);
+ if (IS_BLOCK(slot->type))
+ {
+ offset = start_sector;
+ fvdbg("nbytes=%d sector offset=%d\n", nbytes, offset);
+ }
+ else
+ {
+ offset = start_sector * SECTORSIZE(slot);
+ fvdbg("nbytes=%d byte offset=%d\n", nbytes, offset);
+ }
+ mmcsd_dumpbuffer("Write buffer", buffer, nbytes);
+
+ /* Select the slave */
+
+ mmcsd_semtake(&slot->sem);
+ SPI_SELECT(spi, SPIDEV_MMCSD, true);
+
+ /* Single or multiple block transfer? */
+
+ if (nsectors == 1)
+ {
+ /* Send CMD24 (WRITE_BLOCK) and verify that good R1 status is returned */
+
+ response = mmcsd_sendcmd(slot, &g_cmd24, offset);
+ if (response != MMCSD_SPIR1_OK)
+ {
+ fdbg("CMD24 failed: R1=%02x\n", response);
+ goto errout_with_sem;
+ }
+
+ /* Then transfer the sector */
+
+ if (mmcsd_xmitblock(slot, buffer, SECTORSIZE(slot), 0xfe) != 0)
+ {
+ fdbg("Block transfer failed\n");
+ goto errout_with_sem;
+ }
+ }
+ else
+ {
+ /* Set the number of blocks to be pre-erased (SD only) */
+
+ if (IS_SD(slot->type))
+ {
+ response = mmcsd_sendcmd(slot, &g_acmd23, nsectors);
+ if (response != MMCSD_SPIR1_OK)
+ {
+ fdbg("ACMD23 failed: R1=%02x\n", response);
+ goto errout_with_sem;
+ }
+ }
+
+ /* Send CMD25: Continuously write blocks of data until the
+ * tranmission is stopped.
+ */
+
+ response = mmcsd_sendcmd(slot, &g_cmd25, offset);
+ if (response != MMCSD_SPIR1_OK)
+ {
+ fdbg("CMD25 failed: R1=%02x\n", response);
+ goto errout_with_sem;
+ }
+
+ /* Transmit each block */
+
+ for (i = 0; i < nsectors; i++)
+ {
+ if (mmcsd_xmitblock(slot, buffer, SECTORSIZE(slot), 0xfc) != 0)
+ {
+ fdbg("Failed: to receive the block\n");
+ goto errout_with_sem;
+ }
+ buffer += SECTORSIZE(slot);
+ }
+
+ /* Send the stop transmission token */
+
+ SPI_SEND(spi, MMCSD_SPIDT_STOPTRANS);
+ }
+
+ /* Wait until the card is no longer busy */
+
+ ret = mmcsd_waitready(slot);
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ SPI_SEND(spi, 0xff);
+ mmcsd_semgive(&slot->sem);
+
+ /* The success return value is the number of sectors written */
+
+ return nsectors;
+
+errout_with_sem:
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ mmcsd_semgive(&slot->sem);
+ return -EIO;
+}
+#endif
+
+/****************************************************************************
+ * Name: mmcsd_geometry
+ *
+ * Description:
+ * Return device geometry
+ *
+ ****************************************************************************/
+
+static int mmcsd_geometry(FAR struct inode *inode, struct geometry *geometry)
+{
+ FAR struct mmcsd_slot_s *slot;
+ FAR struct spi_dev_s *spi;
+ uint8_t csd[16];
+ int ret;
+
+#ifdef CONFIG_DEBUG
+ if (!geometry)
+ {
+ fdbg("Invalid parameters\n");
+ return -EINVAL;
+ }
+
+ if (!inode || !inode->i_private)
+ {
+ fdbg("Internal confusion\n");
+ return -EIO;
+ }
+#endif
+
+ /* Extract our private data from the inode structure */
+
+ slot = (FAR struct mmcsd_slot_s *)inode->i_private;
+ spi = slot->spi;
+
+#ifdef CONFIG_DEBUG
+ if (!spi)
+ {
+ fdbg("Internal confusion\n");
+ return -EIO;
+ }
+#endif
+
+ /* Re-sample the CSD */
+
+ mmcsd_semtake(&slot->sem);
+ SPI_SELECT(spi, SPIDEV_MMCSD, true);
+ ret = mmcsd_getcsd(slot, csd);
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+
+ if (ret < 0)
+ {
+ mmcsd_semgive(&slot->sem);
+ fdbg("mmcsd_getcsd returned %d\n", ret);
+ return ret;
+ }
+
+ /* Check for changes related to write protection */
+
+ mmcsd_checkwrprotect(slot, csd);
+
+ /* Then return the card geometry */
+
+ geometry->geo_available =
+ ((slot->state & (MMCSD_SLOTSTATUS_NOTREADY|MMCSD_SLOTSTATUS_NODISK)) == 0);
+ geometry->geo_mediachanged =
+ ((slot->state & MMCSD_SLOTSTATUS_MEDIACHGD) != 0);
+#if defined(CONFIG_FS_WRITABLE) && !defined(CONFIG_MMCSD_READONLY)
+ geometry->geo_writeenabled =
+ ((slot->state & MMCSD_SLOTSTATUS_WRPROTECT) == 0);
+#else
+ geometry->geo_writeenabled = false;
+#endif
+ geometry->geo_nsectors = slot->nsectors;
+ geometry->geo_sectorsize = SECTORSIZE(slot);
+
+ /* After reporting mediachanged, clear the indication so that it is not
+ * reported again.
+ */
+
+ slot->state &= ~MMCSD_SLOTSTATUS_MEDIACHGD;
+ mmcsd_semgive(&slot->sem);
+
+ fvdbg("geo_available: %d\n", geometry->geo_available);
+ fvdbg("geo_mediachanged: %d\n", geometry->geo_mediachanged);
+ fvdbg("geo_writeenabled: %d\n", geometry->geo_writeenabled);
+ fvdbg("geo_nsectors: %d\n", geometry->geo_nsectors);
+ fvdbg("geo_sectorsize: %d\n", geometry->geo_sectorsize);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Initialization
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mmcsd_mediainitialize
+ *
+ * Description:
+ * Detect media and initialize
+ *
+ ****************************************************************************/
+
+static int mmcsd_mediainitialize(FAR struct mmcsd_slot_s *slot)
+{
+ FAR struct spi_dev_s *spi = slot->spi;
+ uint8_t csd[16];
+ uint32_t result = MMCSD_SPIR1_IDLESTATE;
+ uint32_t start;
+ uint32_t elapsed;
+ int i, j;
+
+ /* Assume that the card is not ready (we'll clear this on successful card
+ * initialization.
+ */
+
+ slot->state |= MMCSD_SLOTSTATUS_NOTREADY;
+
+ /* Check if there is a card present in the slot. This is normally a matter is
+ * of GPIO sensing and does not really involve SPI, but by putting this
+ * functionality in the SPI interface, we encapuslate the SPI MMC/SD
+ * interface
+ */
+
+ if ((SPI_STATUS(spi, SPIDEV_MMCSD) & SPI_STATUS_PRESENT) == 0)
+ {
+ fdbg("No card present\n");
+ slot->state |= MMCSD_SLOTSTATUS_NODISK;
+ return -ENODEV;
+ }
+
+ /* Clock Freq. Identification Mode < 400kHz */
+
+ SPI_SETFREQUENCY(spi, MMCSD_IDMODE_CLOCK);
+
+ /* Set the maximum access time out */
+
+ slot->taccess = SD_READACCESS;
+
+ /* The SD card wakes up in SD mode. It will enter SPI mode if the chip select signal is
+ * asserted (negative) during the reception of the reset command (CMD0) and the card is in
+ * IDLE state.
+ */
+
+ for (i = 0; i < 2; i++)
+ {
+ /* After power up at least 74 clock cycles are required prior to
+ * starting bus communication
+ */
+
+ for (j = 10; j; j--)
+ {
+ SPI_SEND(spi, 0xff);
+ }
+
+ /* Send CMD0 (GO_TO_IDLE) with CS asserted to put MMC/SD in
+ * IDLE/SPI mode. Return from CMD0 is R1 which should now
+ * show IDLE STATE
+ */
+
+ fvdbg("Send CMD0\n");
+ SPI_SELECT(spi, SPIDEV_MMCSD, true);
+ result = mmcsd_sendcmd(slot, &g_cmd0, 0);
+ if (result == MMCSD_SPIR1_IDLESTATE)
+ {
+ /* Break out of the loop with card selected */
+
+ fvdbg("Card is in IDLE state\n");
+ break;
+ }
+
+ /* De-select card and try again */
+
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ }
+
+ /* Verify that we exit the above loop with the card reporting IDLE state */
+
+ if (result != MMCSD_SPIR1_IDLESTATE)
+ {
+ fdbg("Send CMD0 failed: R1=%02x\n", result);
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ return -EIO;
+ }
+
+ slot->type = MMCSD_CARDTYPE_UNKNOWN;
+
+ /* Check for SDHC Version 2.x. CMD 8 is reserved on SD version 1.0 and MMC. */
+
+ fvdbg("Send CMD8\n");
+ result = mmcsd_sendcmd(slot, &g_cmd8, 0x1aa);
+ if (result == MMCSD_SPIR1_IDLESTATE)
+ {
+ /* Verify the operating voltage and that the 0xaa was correctly echoed */
+
+ if (((slot->r7 & MMCSD_SPIR7_VOLTAGE_MASK) == MMCSD_SPIR7_VOLTAGE_27) &&
+ ((slot->r7 & MMCSD_SPIR7_ECHO_MASK) == 0xaa))
+ {
+ /* Try CMD55/ACMD41 for up to 1 second or until the card exits
+ * the IDLE state
+ */
+
+ start = START_TIME;
+ elapsed = 0;
+ do
+ {
+ fvdbg("%d. Send CMD55/ACMD41\n", elapsed);
+ result = mmcsd_sendcmd(slot, &g_cmd55, 0);
+ if (result == MMCSD_SPIR1_IDLESTATE || result == MMCSD_SPIR1_OK)
+ {
+ result = mmcsd_sendcmd(slot, &g_acmd41, (uint32_t)1 << 30);
+ if (result == MMCSD_SPIR1_OK)
+ {
+ break;
+ }
+ }
+ elapsed = ELAPSED_TIME(start);
+ }
+ while (elapsed < MMCSD_DELAY_1SEC);
+
+ /* Check if ACMD41 was sent successfully */
+
+ if (elapsed < MMCSD_DELAY_1SEC)
+ {
+ fvdbg("Send CMD58\n");
+ SPI_SEND(spi, 0xff);
+ result = mmcsd_sendcmd(slot, &g_cmd58, 0);
+ if (result == MMCSD_SPIR1_OK)
+ {
+ fvdbg("OCR: %08x\n", slot->ocr);
+ if ((slot->ocr & MMCSD_OCR_CCS) != 0)
+ {
+ fdbg("Identified SD ver2 card/with block access\n");
+ slot->type = MMCSD_CARDTYPE_SDV2|MMCSD_CARDTYPE_BLOCK;
+ }
+ else
+ {
+ fdbg("Identified SD ver2 card\n");
+ slot->type = MMCSD_CARDTYPE_SDV2;
+ }
+ }
+ }
+ }
+ }
+
+ /* Check for SDC version 1.x or MMC */
+
+ else
+ {
+ /* Both the MMC card and the SD card support CMD55 */
+
+ fvdbg("Send CMD55/ACMD41\n");
+ result = mmcsd_sendcmd(slot, &g_cmd55, 0);
+ if (result == MMCSD_SPIR1_IDLESTATE || result == MMCSD_SPIR1_OK)
+ {
+ /* But ACMD41 is supported only on SD */
+
+ result = mmcsd_sendcmd(slot, &g_acmd41, 0);
+ if (result == MMCSD_SPIR1_IDLESTATE || result == MMCSD_SPIR1_OK)
+ {
+ fdbg("Identified SD ver1 card\n");
+ slot->type = MMCSD_CARDTYPE_SDV1;
+ }
+ }
+
+ /* Make sure that we are out of the Idle state */
+
+ start = START_TIME;
+ elapsed = 0;
+ do
+ {
+ if (IS_SD(slot->type))
+ {
+ fvdbg("%d. Send CMD55/ACMD41\n", elapsed);
+ result = mmcsd_sendcmd(slot, &g_cmd55, 0);
+ if (result == MMCSD_SPIR1_IDLESTATE || result == MMCSD_SPIR1_OK)
+ {
+ result = mmcsd_sendcmd(slot, &g_acmd41, 0);
+ if (result == MMCSD_SPIR1_OK)
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ fvdbg("%d. Send CMD1\n", i);
+ result = mmcsd_sendcmd(slot, &g_cmd1, 0);
+ if (result == MMCSD_SPIR1_OK)
+ {
+ fdbg("%d. Identified MMC card\n", i);
+ slot->type = MMCSD_CARDTYPE_MMC;
+ break;
+ }
+ }
+ elapsed = ELAPSED_TIME(start);
+ }
+ while (elapsed < MMCSD_DELAY_1SEC);
+
+ if (elapsed >= MMCSD_DELAY_1SEC)
+ {
+ fdbg("Failed to exit IDLE state\n");
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ return -EIO;
+ }
+ }
+
+ if (slot->type == MMCSD_CARDTYPE_UNKNOWN)
+ {
+ fdbg("Failed to identify card\n");
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ return -EIO;
+ }
+
+ /* Read CSD. CSD must always be valid */
+
+ fvdbg("Get CSD\n");
+ result = mmcsd_getcsd(slot, csd);
+ if (result != OK)
+ {
+ fdbg("mmcsd_getcsd(CMD9) failed: %d\n", result);
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ return -EIO;
+ }
+ mmcsd_dmpcsd(csd, slot->type);
+
+ /* CSD data and set block size */
+
+ mmcsd_decodecsd(slot, csd);
+ mmcsd_checkwrprotect(slot, csd);
+
+ /* SDHC ver2.x cards have fixed block transfer size of 512 bytes. SDC
+ * ver1.x cards with capacity less than 1Gb, will have sector size
+ * 512 byes. SDC ver1.x cards with capacity of 2Gb will report readbllen
+ * of 1024 but should use 512 bytes for block transfers. SDC ver1.x 4Gb
+ * cards will report readbllen of 2048 bytes -- are they also 512 bytes?
+ * I think that none of these high capacity cards support setting the
+ * block length??
+ */
+
+#ifdef CONFIG_MMCSD_SECTOR512
+ /* Using 512 byte sectors, the maximum ver1.x capacity is 4096 x 512 blocks.
+ * The saved slot->nsectors is converted to 512 byte blocks, so if slot->nsectors
+ * exceeds 4096 x 512, then we must be dealing with a card with read_bl_len
+ * of 1024 or 2048.
+ */
+
+ if (!IS_SDV2(slot->type) && slot->nsectors <= ((uint32_t)4096*12))
+ {
+ /* Don't set the block len on high capacity cards (ver1.x or ver2.x) */
+
+ mmcsd_setblklen(slot, SECTORSIZE(slot));
+ }
+#else
+ if (!IS_SDV2(slot->type))
+ {
+ /* Don't set the block len on ver2.x cards */
+
+ mmcsd_setblklen(slot, SECTORSIZE(slot));
+ }
+#endif
+
+ slot->state &= ~MMCSD_SLOTSTATUS_NOTREADY;
+ SPI_SELECT(spi, SPIDEV_MMCSD, false);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mmcsd_mediachanged
+ *
+ * Description:
+ * Handle initialization/media change events
+ *
+ ****************************************************************************/
+
+static void mmcsd_mediachanged(void *arg)
+{
+ struct mmcsd_slot_s *slot = (struct mmcsd_slot_s*)arg;
+ FAR struct spi_dev_s *spi;
+ uint8_t oldstate;
+ int ret;
+
+#ifdef CONFIG_DEBUG
+ if (!slot || !slot->spi)
+ {
+ fdbg("Internal confusion\n");
+ return;
+ }
+#endif
+ spi = slot->spi;
+
+ /* Save the current slot state and reassess the new state */
+
+ mmcsd_semtake(&slot->sem);
+ oldstate = slot->state;
+
+ /* Check if media was removed or inserted */
+
+ slot->state &= ~(MMCSD_SLOTSTATUS_NODISK|MMCSD_SLOTSTATUS_NOTREADY|MMCSD_SLOTSTATUS_MEDIACHGD);
+ if ((SPI_STATUS(spi, SPIDEV_MMCSD) & SPI_STATUS_PRESENT) == 0)
+ {
+ /* Media is not present */
+
+ fdbg("No card present\n");
+ slot->state |= (MMCSD_SLOTSTATUS_NODISK|MMCSD_SLOTSTATUS_NOTREADY);
+
+ /* Was media removed? */
+
+ if ((oldstate & MMCSD_SLOTSTATUS_NODISK) == 0)
+ {
+ slot->state |= MMCSD_SLOTSTATUS_MEDIACHGD;
+ }
+ }
+
+ /* Media is present, was it just inserted? Or, if it was previously not ready,
+ * then try re-initializing it
+ */
+
+ else if ((oldstate & (MMCSD_SLOTSTATUS_NODISK|MMCSD_SLOTSTATUS_NOTREADY)) != 0)
+ {
+ /* (Re-)ininitialize for the media in the slot */
+
+ ret = mmcsd_mediainitialize(slot);
+ if (ret == 0)
+ {
+ fvdbg("mmcsd_mediainitialize returned OK\n");
+ slot->state |= MMCSD_SLOTSTATUS_MEDIACHGD;
+ }
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mmcsd_spislotinitialize
+ *
+ * Description:
+ * Initialize one slot for operation using the SPI MMC/SD interface
+ *
+ * Input Parameters:
+ * minor - The MMC/SD minor device number. The MMC/SD device will be
+ * registered as /dev/mmcsdN where N is the minor number
+ * slotno - The slot number to use. This is only meaningful for architectures
+ * that support multiple MMC/SD slots. This value must be in the range
+ * {0, ..., CONFIG_MMCSD_NSLOTS}.
+ * spi - And instance of an SPI interface obtained by called
+ * up_spiinitialize() with the appropriate port number (see spi.h)
+ *
+ ****************************************************************************/
+
+int mmcsd_spislotinitialize(int minor, int slotno, FAR struct spi_dev_s *spi)
+{
+ struct mmcsd_slot_s *slot;
+ char devname[16];
+ int ret;
+
+#ifdef CONFIG_DEBUG
+ if ((unsigned)slotno >= CONFIG_MMCSD_NSLOTS || (unsigned)minor > 255 || !spi)
+ {
+ fdbg("Invalid arguments\n");
+ return -EINVAL;
+ }
+#endif
+
+ /* Select the slot structure */
+
+ slot = &g_mmcsdslot[slotno];
+ memset(slot, 0, sizeof(struct mmcsd_slot_s));
+ sem_init(&slot->sem, 0, 1);
+
+#ifdef CONFIG_DEBUG
+ if (slot->spi)
+ {
+ fdbg("Already registered\n");
+ return -EBUSY;
+ }
+#endif
+
+ /* Bind the SPI port to the slot */
+
+ slot->spi = spi;
+
+ /* Ininitialize for the media in the slot (if any) */
+
+ ret = mmcsd_mediainitialize(slot);
+ if (ret == 0)
+ {
+ fvdbg("mmcsd_mediainitialize returned OK\n");
+ slot->state |= MMCSD_SLOTSTATUS_MEDIACHGD;
+ }
+
+ /* Create a MMC/SD device name */
+
+ snprintf(devname, 16, "/dev/mmcsd%d", minor);
+
+ /* Register the driver, even on a failure condition. A
+ * card may be inserted later, for example.
+ */
+
+ ret = register_blockdriver(devname, &g_bops, MMCSD_MODE, slot);
+ if (ret < 0)
+ {
+ fdbg("register_blockdriver failed: %d\n", -ret);
+ slot->spi = NULL;
+ return ret;
+ }
+
+ /* Register a media change callback to handler insertion and
+ * removal of cards.
+ */
+
+ (void)SPI_REGISTERCALLBACK(spi, mmcsd_mediachanged, (void*)slot);
+ return OK;
+}
diff --git a/nuttx/drivers/mmcsd/mmcsd_spi.h b/nuttx/drivers/mmcsd/mmcsd_spi.h
new file mode 100644
index 000000000..8c6f9bae7
--- /dev/null
+++ b/nuttx/drivers/mmcsd/mmcsd_spi.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+ * drivers/mmcsd/mmcsd_spi.h
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_MMCSD_MMCSD_SPI_H
+#define __DRIVERS_MMCSD_MMCSD_SPI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+/* SPI *******************************************************************/
+
+/* SPI Command Set */
+
+#define CMD0 0x40 /* GO_IDLE_STATE: Resets all cards to idle state */
+#define CMD1 0x41 /* SEND_OP_COND: Sends capacity support information */
+#define CMD6 0x46 /* SWITCH_FUNC: Checks switchable function */
+#define CMD8 0x48 /* SEND_IF_COND: Sends SD Memory Card interface condition */
+#define CMD9 0x49 /* SEND_CSD: Asks card to send its card specific data (CSD) */
+#define CMD10 0x4a /* SEND_CID: Asks card to send its card identification (CID) */
+#define CMD12 0x4c /* STOP_TRANSMISSION: Forces the card to stop transmission */
+#define CMD13 0x4d /* SEND_STATUS: Asks card to send its status register */
+#define CMD16 0x50 /* SET_BLOCKLEN: Sets a block length (in bytes) */
+#define CMD17 0x51 /* READ_SINGLE_BLOCK: Reads a block of the selected size */
+#define CMD18 0x52 /* READ_MULTIPLE_BLOCK: Continuously transfers blocks from card to host */
+#define CMD20 0x54 /* CMD_WRITEBLOCK: Write block to memory (MMC) */
+#define CMD24 0x58 /* WRITE_BLOCK: Writes a block of the selected size */
+#define CMD25 0x59 /* WRITE_MULTIPLE_BLOCK: Continuously writes blocks of data */
+#define CMD27 0x5b /* PROGRAM_CSD: Set programmable bits of the CSD */
+#define CMD28 0x5c /* SET_WRITE_PROT: Sets the write protection bit of group */
+#define CMD29 0x5d /* CLR_WRITE_PROT: Clears the write protection bit of group */
+#define CMD30 0x5e /* SEND_WRITE_PROT: Asks card to send state of write protection bits */
+#define CMD32 0x60 /* ERASE_WR_BLK_START_ADDR: Sets address of first block to erase */
+#define CMD33 0x61 /* ERASE_WR_BLK_END_ADDR: Sets address of last block to erase */
+#define CMD34 0x62 /* UNTAG_SECTOR: (MMC) */
+#define CMD35 0x63 /* TAG_ERASE_GROUP_START: (MMC) */
+#define CMD36 0x64 /* TAG_ERASE_GOUPR_END: (MMC) */
+#define CMD37 0x65 /* UNTAG_ERASE_GROUP: (MMC) */
+#define CMD38 0x66 /* ERASE: Erases all previously selected write blocks */
+#define CMD40 0x68 /* CRC_ON_OFF: (MMC) */
+#define CMD42 0x6a /* LOCK_UNLOCK: Used to Set/Reset the Password or lock/unlock card */
+#define CMD55 0x77 /* APP_CMD: Tells card that the next command is an application specific command */
+#define CMD56 0x78 /* GEN_CMD: Used transfer a block to or get block from card */
+#define CMD58 0x7a /* READ_OCR :Reads the OCR register of a card */
+#define CMD59 0x7b /* CRC_ON_OFF: Turns the CRC option on or off */
+#define ACMD13 0x4d /* SD_STATUS: Send the SD Status */
+#define ACMD22 0x56 /* SEND_NUM_WR_BLOCKS: Send number of the errorfree blocks */
+#define ACMD23 0x57 /* SET_WR_BLK_ERASE_COUNT: Set number blocks to erase before writing */
+#define ACMD41 0x69 /* SD_SEND_OP_COND: Sends host capacity support information */
+#define ACMD42 0x6a /* SET_CLR_CARD_DETECT: Connect/disconnect pull-up resistor on CS */
+#define ACMD51 0x73 /* SEND_SCR: Reads the SD Configuration Register (SCR) */
+
+/* SPI 8-bit R1 response */
+
+#define MMCSD_SPIR1_OK 0x00 /* No error bits set */
+#define MMCSD_SPIR1_IDLESTATE 0x01 /* Idle state */
+#define MMCSD_SPIR1_ERASERESET 0x02 /* Erase reset */
+#define MMCSD_SPIR1_ILLEGALCMD 0x04 /* Illegal command */
+#define MMCSD_SPIR1_CRCERROR 0x08 /* Com CRC error */
+#define MMCSD_SPIR1_ERASEERROR 0x10 /* Erase sequence error */
+#define MMCSD_SPIR1_ADDRERROR 0x20 /* Address error */
+#define MMCSD_SPIR1_PARAMERROR 0x40 /* Parameter error */
+
+/* SPI 8-bit R2 response */
+
+#define MMCSD_SPIR2_CARDLOCKED 0x0001 /* Card is locked */
+#define MMCSD_SPIR2_WPERASESKIP 0x0002 /* WP erase skip */
+#define MMCSD_SPIR2_LOCKFAIL 0x0002 /* Lock/unlock cmd failed */
+#define MMCSD_SPIR2_ERROR 0x0004 /* Error */
+#define MMCSD_SPIR2_CCERROR 0x0008 /* CC error */
+#define MMCSD_SPIR2_CARDECCFAIL 0x0010 /* Card ECC failed */
+#define MMCSD_SPIR2_WPVIOLATION 0x0020 /* WP violoation */
+#define MMCSD_SPIR2_ERASEPARAM 0x0040 /* Erase parameter */
+#define MMCSD_SPIR2_OUTOFRANGE 0x0080 /* Out of range */
+#define MMCSD_SPIR2_CSDOVERWRITE 0x0080 /* CSD overwrite */
+#define MMCSD_SPIR2_IDLESTATE 0x0100 /* In idle state */
+#define MMCSD_SPIR2_ERASERESET 0x0200 /* Erase reset */
+#define MMCSD_SPIR2_ILLEGALCMD 0x0400 /* Illegal command */
+#define MMCSD_SPIR2_CRCERROR 0x0800 /* Com CRC error */
+#define MMCSD_SPIR2_ERASEERROR 0x1000 /* Erase sequence error */
+#define MMCSD_SPIR2_ADDRERROR 0x2000 /* Address error */
+#define MMCSD_SPIR2_PARAMERROR 0x4000 /* Parameter error */
+
+/* Last 4 bytes of the 5 byte R7 response */
+
+#define MMCSD_SPIR7_VERSION_SHIFT (28) /* Bits 28-31: Command version number */
+#define MMCSD_SPIR7_VERSION_MASK ((uint32_t)0x0f << MMCSD_SPIR7_VERSION_SHIFT)
+#define MMCSD_SPIR7_VOLTAGE_SHIFT (8) /* Bits 8-11: Voltage accepted */
+#define MMCSD_SPIR7_VOLTAGE_MASK ((uint32_t)0x0f << MMCSD_SPIR7_VOLTAGE_SHIFT)
+#define MMCSD_SPIR7_VOLTAGE_27 ((uint32_t)0x01 << MMCSD_SPIR7_VOLTAGE_SHIFT) /* 2.7-3.6V */
+#define MMCSD_SPIR7_ECHO_SHIFT (0) /* Bits 0-7: Echoed check pattern */
+#define MMCSD_SPIR7_ECHO_MASK ((uint32_t)0xff << MMCSD_SPIR7_ECHO_SHIFT)
+
+/* Data Response */
+
+#define MMCSD_SPIDR_MASK 0x1f /* Mask for valid data response bits */
+#define MMCSD_SPIDR_ACCEPTED 0x05 /* Data accepted */
+#define MMCSD_SPIDR_CRCERROR 0x0b /* Data rejected due to CRC error */
+#define MMCSD_SPIDR_WRERROR 0x0d /* Data rejected due to write error */
+
+/* Data Tokens */
+
+#define MMCSD_SPIDT_STARTBLKSNGL 0xfe /* First byte of block, single block */
+#define MMCSD_SPIDT_STARTBLKMULTI 0xfc /* First byte of block, multi-block */
+#define MMCSD_SPIDT_STOPTRANS 0xfd /* Stop transmission */
+
+/* Data error token */
+
+#define MMCSD_SPIDET_UPPER 0xf0 /* The upper four bits are zero */
+#define MMCSD_SPIDET_ERROR 0x01 /* Error */
+#define MMCSD_SPIDET_CCERROR 0x02 /* CC error */
+#define MMCSD_SPIDET_CARDECCFAIL 0x04 /* Card ECC failed */
+#define MMCSD_SPIDET_OUTOFRANGE 0x08 /* Out of range */
+
+/* Operating Conditions register */
+
+#define MMCSD_OCR_V27 ((uint32_t)1 << 15) /* Bit 15: 2.7-2.8V */
+#define MMCSD_OCR_V28 ((uint32_t)1 << 16) /* Bit 16: 2.8-2.9V */
+#define MMCSD_OCR_V29 ((uint32_t)1 << 17) /* Bit 17: 2.9-3.0V */
+#define MMCSD_OCR_V30 ((uint32_t)1 << 18) /* Bit 18: 3.0-3.1V */
+#define MMCSD_OCR_V31 ((uint32_t)1 << 19) /* Bit 19: 3.1-3.2V */
+#define MMCSD_OCR_V32 ((uint32_t)1 << 20) /* Bit 20: 3.2-3.3V */
+#define MMCSD_OCR_V33 ((uint32_t)1 << 21) /* Bit 21: 3.3-3.4V */
+#define MMCSD_OCR_V34 ((uint32_t)1 << 22) /* Bit 22: 3.4-3.5V */
+#define MMCSD_OCR_V35 ((uint32_t)1 << 23) /* Bit 23: 3.5-3.6V */
+#define MMCSD_OCR_CCS ((uint32_t)1 << 30) /* Bit 30: Card capacity status */
+#define MMCSD_OCR_BUSY ((uint32_t)1 << 31) /* Bit 31: Card powered up status bit */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+#endif /* __DRIVERS_MMCSD_MMCSD_SPI_H */
diff --git a/nuttx/drivers/mtd/Kconfig b/nuttx/drivers/mtd/Kconfig
new file mode 100644
index 000000000..bda5afa84
--- /dev/null
+++ b/nuttx/drivers/mtd/Kconfig
@@ -0,0 +1,68 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+config MTD_AT24XX
+ bool "I2C-based AT24XX eeprom"
+ default n
+ select I2C
+
+config AT24XX_SIZE
+ int "at24xx size(kByte)"
+ default 64
+ depends on MTD_AT24XX
+
+config AT24XX_ADDR
+ hex "at24xx i2c address"
+ default 0x50
+ depends on MTD_AT24XX
+
+config MTD_AT45DB
+ bool "SPI-based AT45DB flash"
+ default n
+ select SPI
+
+config AT45DB_FREQUENCY
+ int "at45db frequency"
+ default 1000000
+ depends on MTD_AT45DB
+
+config AT45DB_PREWAIT
+ bool "enables higher performance write logic"
+ default y
+ depends on MTD_AT45DB
+
+config AT45DB_PWRSAVE
+ bool "enables power save"
+ default n
+ depends on MTD_AT45DB
+
+config MTD_MP25P
+ bool "SPI-based M25P1 falsh"
+ default n
+ select SPI
+
+config MP25P_SPIMODE
+ int "mp25p spi mode"
+ default 0
+ depends on MTD_MP25P
+
+config MP25P_MANUFACTURER
+ hex "mp25p manufacturers ID"
+ default 0x20
+ depends on MTD_MP25P
+ ---help---
+ Various manufacturers may have produced the parts. 0x20 is the manufacturer ID
+ for the STMicro MP25x serial FLASH. If, for example, you are using the a Macronix
+ International MX25 serial FLASH, the correct manufacturer ID would be 0xc2.
+
+config MTD_RAMTRON
+ bool "SPI-based RAMTRON NVRAM Devices FM25V10"
+ default n
+ select SPI
+ ---help---
+ SPI-based RAMTRON NVRAM Devices FM25V10
+
+config MTD_RAM
+ bool "Memory bus ram"
+ default n
diff --git a/nuttx/drivers/mtd/Make.defs b/nuttx/drivers/mtd/Make.defs
new file mode 100644
index 000000000..866d7c713
--- /dev/null
+++ b/nuttx/drivers/mtd/Make.defs
@@ -0,0 +1,54 @@
+############################################################################
+# drivers/mtd/Make.defs
+# These driver supports various Memory Technology Devices (MTD) using the
+# NuttX MTD interface.
+#
+# Copyright (C) 2009-2012 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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 MTD drivers
+
+CSRCS += at45db.c flash_eraseall.c ftl.c m25px.c rammtd.c ramtron.c
+
+ifeq ($(CONFIG_MTD_AT24XX),y)
+CSRCS += at24xx.c
+endif
+
+ifeq ($(CONFIG_MTD_SST25),y)
+CSRCS += sst25.c
+endif
+
+# Include MTD driver support
+
+DEPPATH += --dep-path mtd
+VPATH += :mtd
+
diff --git a/nuttx/drivers/mtd/at24xx.c b/nuttx/drivers/mtd/at24xx.c
new file mode 100644
index 000000000..d157a9c47
--- /dev/null
+++ b/nuttx/drivers/mtd/at24xx.c
@@ -0,0 +1,429 @@
+/************************************************************************************
+ * drivers/mtd/at24xx.c
+ * Driver for I2C-based at24cxx EEPROM(at24c32,at24c64,at24c128,at24c256)
+ *
+ * Copyright (C) 2011 Li Zhuoyi. All rights reserved.
+ * Author: Li Zhuoyi <lzyy.cn@gmail.com>
+ * History: 0.1 2011-08-20 initial version
+ *
+ * 2011-11-1 Added support for larger MTD block sizes: Hal Glenn <hglenn@2g-eng.com>
+ *
+ * Derived from drivers/mtd/m25px.c
+ *
+ * Copyright (C) 2009-2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ************************************************************************************/
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/i2c.h>
+#include <nuttx/mtd.h>
+
+#ifdef CONFIG_MTD_AT24XX
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+
+/* As a minimum, the size of the AT24 part and its 7-bit I2C address are required. */
+
+#ifndef CONFIG_AT24XX_SIZE
+# warning "Assuming AT24 size 64"
+# define CONFIG_AT24XX_SIZE 64
+#endif
+#ifndef CONFIG_AT24XX_ADDR
+# warning "Assuming AT24 address of 0x50"
+# define CONFIG_AT24XX_ADDR 0x50
+#endif
+
+/* Get the part configuration based on the size configuration */
+
+#if CONFIG_AT24XX_SIZE == 32
+# define AT24XX_NPAGES 128
+# define AT24XX_PAGESIZE 32
+#elif CONFIG_AT24XX_SIZE == 48
+# define AT24XX_NPAGES 192
+# define AT24XX_PAGESIZE 32
+#elif CONFIG_AT24XX_SIZE == 64
+# define AT24XX_NPAGES 256
+# define AT24XX_PAGESIZE 32
+#elif CONFIG_AT24XX_SIZE == 128
+# define AT24XX_NPAGES 256
+# define AT24XX_PAGESIZE 64
+#elif CONFIG_AT24XX_SIZE == 256
+# define AT24XX_NPAGES 512
+# define AT24XX_PAGESIZE 64
+#endif
+
+/* For applications where a file system is used on the AT24, the tiny page sizes
+ * will result in very inefficient FLASH usage. In such cases, it is better if
+ * blocks are comprised of "clusters" of pages so that the file system block
+ * size is, say, 256 or 512 bytes. In any event, the block size *must* be an
+ * even multiple of the pages.
+ */
+
+#ifndef CONFIG_AT24XX_MTD_BLOCKSIZE
+# warning "Assuming driver block size is the same as the FLASH page size"
+# define CONFIG_AT24XX_MTD_BLOCKSIZE AT24XX_PAGESIZE
+#endif
+
+/************************************************************************************
+ * Private Types
+ ************************************************************************************/
+
+/* This type represents the state of the MTD device. The struct mtd_dev_s
+ * must appear at the beginning of the definition so that you can freely
+ * cast between pointers to struct mtd_dev_s and struct at24c_dev_s.
+ */
+
+struct at24c_dev_s
+{
+ struct mtd_dev_s mtd; /* MTD interface */
+ FAR struct i2c_dev_s *dev; /* Saved I2C interface instance */
+ uint8_t addr; /* I2C address */
+ uint16_t pagesize; /* 32, 63 */
+ uint16_t npages; /* 128, 256, 512, 1024 */
+};
+
+/************************************************************************************
+ * Private Function Prototypes
+ ************************************************************************************/
+
+/* MTD driver methods */
+
+static int at24c_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
+static ssize_t at24c_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR uint8_t *buf);
+static ssize_t at24c_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buf);
+static ssize_t at24c_read(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes,FAR uint8_t *buffer);
+static int at24c_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
+
+/************************************************************************************
+ * Private Data
+ ************************************************************************************/
+
+/* At present, only a signal AT24 part is supported. In this case, a statically
+ * allocated state structure may be used.
+ */
+
+static struct at24c_dev_s g_at24c;
+
+/************************************************************************************
+ * Private Functions
+ ************************************************************************************/
+
+static int at24c_eraseall(FAR struct at24c_dev_s *priv)
+{
+ int startblock = 0;
+ uint8_t buf[AT24XX_PAGESIZE + 2];
+
+ memset(&buf[2],0xff,priv->pagesize);
+ I2C_SETADDRESS(priv->dev,priv->addr,7);
+ I2C_SETFREQUENCY(priv->dev,100000);
+
+ for (startblock = 0; startblock < priv->npages; startblock++)
+ {
+ uint16_t offset = startblock * priv->pagesize;
+ buf[1] = offset & 0xff;
+ buf[0] = (offset >> 8) & 0xff;
+
+ while (I2C_WRITE(priv->dev, buf, 2) < 0)
+ {
+ usleep(1000);
+ }
+ I2C_WRITE(priv->dev, buf, priv->pagesize + 2);
+ }
+
+ return OK;
+}
+
+/************************************************************************************
+ * Name: at24c_erase
+ ************************************************************************************/
+
+static int at24c_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
+{
+ /* EEprom need not erase */
+
+ return (int)nblocks;
+}
+
+/************************************************************************************
+ * Name: at24c_bread
+ ************************************************************************************/
+
+static ssize_t at24c_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR uint8_t *buffer)
+{
+ FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev;
+ size_t blocksleft;
+
+#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
+ startblock *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
+ nblocks *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
+#endif
+ blocksleft = nblocks;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ if (startblock >= priv->npages)
+ {
+ return 0;
+ }
+
+ if (startblock + nblocks > priv->npages)
+ {
+ nblocks = priv->npages - startblock;
+ }
+
+ I2C_SETADDRESS(priv->dev,priv->addr,7);
+ I2C_SETFREQUENCY(priv->dev,100000);
+
+ while (blocksleft-- > 0)
+ {
+ uint16_t offset = startblock * priv->pagesize;
+ uint8_t buf[2];
+ buf[1] = offset & 0xff;
+ buf[0] = (offset >> 8) & 0xff;
+
+ while (I2C_WRITE(priv->dev, buf, 2) < 0)
+ {
+ fvdbg("wait\n");
+ usleep(1000);
+ }
+
+ I2C_READ(priv->dev, buffer, priv->pagesize);
+ startblock++;
+ buffer += priv->pagesize;
+ }
+
+#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
+ return nblocks / (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
+#else
+ return nblocks;
+#endif
+}
+
+/************************************************************************************
+ * Name: at24c_bwrite
+ *
+ * Operates on MTD block's and translates to FLASH pages
+ *
+ ************************************************************************************/
+
+static ssize_t at24c_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR const uint8_t *buffer)
+{
+ FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev;
+ size_t blocksleft;
+ uint8_t buf[AT24XX_PAGESIZE+2];
+
+#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
+ startblock *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
+ nblocks *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
+#endif
+ blocksleft = nblocks;
+
+ if (startblock >= priv->npages)
+ {
+ return 0;
+ }
+
+ if (startblock + nblocks > priv->npages)
+ {
+ nblocks = priv->npages - startblock;
+ }
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+ I2C_SETADDRESS(priv->dev, priv->addr, 7);
+ I2C_SETFREQUENCY(priv->dev, 100000);
+
+ while (blocksleft-- > 0)
+ {
+ uint16_t offset = startblock * priv->pagesize;
+ while (I2C_WRITE(priv->dev, (uint8_t *)&offset, 2) < 0)
+ {
+ fvdbg("wait\n");
+ usleep(1000);
+ }
+
+ buf[1] = offset & 0xff;
+ buf[0] = (offset >> 8) & 0xff;
+ memcpy(&buf[2], buffer, priv->pagesize);
+
+ I2C_WRITE(priv->dev, buf, priv->pagesize + 2);
+ startblock++;
+ buffer += priv->pagesize;
+ }
+
+#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
+ return nblocks / (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
+#else
+ return nblocks;
+#endif
+}
+
+/************************************************************************************
+ * Name: at24c_ioctl
+ ************************************************************************************/
+
+static int at24c_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
+{
+ FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev;
+ int ret = -EINVAL; /* Assume good command with bad parameters */
+
+ fvdbg("cmd: %d \n", cmd);
+
+ switch (cmd)
+ {
+ case MTDIOC_GEOMETRY:
+ {
+ FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
+ if (geo)
+ {
+ /* Populate the geometry structure with information need to know
+ * the capacity and how to access the device.
+ *
+ * NOTE: that the device is treated as though it where just an array
+ * of fixed size blocks. That is most likely not true, but the client
+ * will expect the device logic to do whatever is necessary to make it
+ * appear so.
+ *
+ * blocksize:
+ * May be user defined. The block size for the at24XX devices may be
+ * larger than the page size in order to better support file systems.
+ * The read and write functions translate BLOCKS to pages for the
+ * small flash devices
+ * erasesize:
+ * It has to be at least as big as the blocksize, bigger serves no
+ * purpose.
+ * neraseblocks
+ * Note that the device size is in kilobits and must be scaled by
+ * 1024 / 8
+ */
+
+#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
+ geo->blocksize = CONFIG_AT24XX_MTD_BLOCKSIZE;
+ geo->erasesize = CONFIG_AT24XX_MTD_BLOCKSIZE;
+ geo->neraseblocks = (CONFIG_AT24XX_SIZE * 1024 / 8) / CONFIG_AT24XX_MTD_BLOCKSIZE;
+#else
+ geo->blocksize = priv->pagesize;
+ geo->erasesize = priv->pagesize;
+ geo->neraseblocks = priv->npages;
+#endif
+ ret = OK;
+
+ fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
+ geo->blocksize, geo->erasesize, geo->neraseblocks);
+ }
+ }
+ break;
+
+ case MTDIOC_BULKERASE:
+ ret=at24c_eraseall(priv);
+ break;
+
+ case MTDIOC_XIPBASE:
+ default:
+ ret = -ENOTTY; /* Bad command */
+ break;
+ }
+
+ return ret;
+}
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: at24c_initialize
+ *
+ * Description:
+ * Create an initialize MTD device instance. MTD devices are not registered
+ * in the file system, but are created as instances that can be bound to
+ * other functions (such as a block or character driver front end).
+ *
+ ************************************************************************************/
+
+FAR struct mtd_dev_s *at24c_initialize(FAR struct i2c_dev_s *dev)
+{
+ FAR struct at24c_dev_s *priv;
+
+ fvdbg("dev: %p\n", dev);
+
+ /* Allocate a state structure (we allocate the structure instead of using
+ * a fixed, static allocation so that we can handle multiple FLASH devices.
+ * The current implementation would handle only one FLASH part per I2C
+ * device (only because of the SPIDEV_FLASH definition) and so would have
+ * to be extended to handle multiple FLASH parts on the same I2C bus.
+ */
+
+ priv = &g_at24c;
+ if (priv)
+ {
+ /* Initialize the allocated structure */
+
+ priv->addr = CONFIG_AT24XX_ADDR;
+ priv->pagesize = AT24XX_PAGESIZE;
+ priv->npages = AT24XX_NPAGES;
+
+ priv->mtd.erase = at24c_erase;
+ priv->mtd.bread = at24c_bread;
+ priv->mtd.bwrite = at24c_bwrite;
+ priv->mtd.ioctl = at24c_ioctl;
+ priv->dev = dev;
+ }
+
+ /* Return the implementation-specific state structure as the MTD device */
+
+ fvdbg("Return %p\n", priv);
+ return (FAR struct mtd_dev_s *)priv;
+}
+
+#endif
diff --git a/nuttx/drivers/mtd/at45db.c b/nuttx/drivers/mtd/at45db.c
new file mode 100644
index 000000000..f3c0c72c1
--- /dev/null
+++ b/nuttx/drivers/mtd/at45db.c
@@ -0,0 +1,899 @@
+/************************************************************************************
+ * drivers/mtd/at45db.c
+ * Driver for SPI-based AT45DB161D (16Mbit)
+ *
+ * Copyright (C) 2010-2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ************************************************************************************/
+
+/* Ordering Code Detail:
+ *
+ * AT 45DB 16 1 D – SS U
+ * | | | | | | `- Device grade
+ * | | | | | `- Package Option
+ * | | | | `- Device revision
+ * | | | `- Interface: 1=serial
+ * | | `- Capacity: 16=16Mbit
+ * | `- Product family
+ * `- Atmel designator
+ */
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/spi.h>
+#include <nuttx/mtd.h>
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+
+/* Configuration ********************************************************************/
+/* CONFIG_AT45DB_PREWAIT enables higher performance write logic: We leave the chip
+ * busy after write and erase operations. This improves write and erase performance
+ * because we do not have to wait as long between transactions (other processing can
+ * occur while the chip is busy) but means that the chip must stay powered:
+ */
+
+#if defined(CONFIG_AT45DB_PWRSAVE) && defined(CONFIG_AT45DB_PREWAIT)
+# error "Both CONFIG_AT45DB_PWRSAVE and CONFIG_AT45DB_PREWAIT are defined"
+#endif
+
+/* If the user has provided no frequency, use 1MHz */
+
+#ifndef CONFIG_AT45DB_FREQUENCY
+# define CONFIG_AT45DB_FREQUENCY 1000000
+#endif
+
+/* SPI Commands *********************************************************************/
+
+/* Read commands */
+
+#define AT45DB_RDMN 0xd2 /* Main Memory Page Read */
+#define AT45DB_RDARRY 0xe8 /* Continuous Array Read (Legacy Command) */
+#define AT45DB_RDARRAYLF 0x03 /* Continuous Array Read (Low Frequency) */
+#define AT45DB_RDARRAYHF 0x0b /* Continuous Array Read (High Frequency) */
+#define AT45DB_RDBF1LF 0xd1 /* Buffer 1 Read (Low Frequency) */
+#define AT45DB_RDBF2LF 0xd3 /* Buffer 2 Read (Low Frequency) */
+#define AT45DB_RDBF1 0xd4 /* Buffer 1 Read */
+#define AT45DB_RDBF2 0xd6 /* Buffer 2 Read */
+
+/* Program and Erase Commands */
+
+#define AT45DB_WRBF1 0x84 /* Buffer 1 Write */
+#define AT45DB_WRBF2 0x87 /* Buffer 2 Write */
+#define AT45DB_BF1TOMNE 0x83 /* Buffer 1 to Main Memory Page Program with Built-in Erase */
+#define AT45DB_BF2TOMNE 0x86 /* Buffer 2 to Main Memory Page Program with Built-in Erase */
+#define AT45DB_BF1TOMN 0x88 /* Buffer 1 to Main Memory Page Program without Built-in Erase */
+#define AT45DB_BF2TOMN 0x89 /* Buffer 2 to Main Memory Page Program without Built-in Erase */
+#define AT45DB_PGERASE 0x81 /* Page Erase */
+#define AT45DB_BLKERASE 0x50 /* Block Erase */
+#define AT45DB_SECTERASE 0x7c /* Sector Erase */
+#define AT45DB_CHIPERASE1 0xc7 /* Chip Erase - byte 1 */
+# define AT45DB_CHIPERASE2 0x94 /* Chip Erase - byte 2 */
+# define AT45DB_CHIPERASE3 0x80 /* Chip Erase - byte 3 */
+# define AT45DB_CHIPERASE4 0x9a /* Chip Erase - byte 4 */
+#define AT45DB_MNTHRUBF1 0x82 /* Main Memory Page Program Through Buffer 1 */
+#define AT45DB_MNTHRUBF2 0x85 /* Main Memory Page Program Through Buffer 2 */
+
+/* Protection and Security Commands */
+
+#define AT45DB_ENABPROT1 0x3d /* Enable Sector Protection - byte 1 */
+# define AT45DB_ENABPROT2 0x2a /* Enable Sector Protection - byte 2 */
+# define AT45DB_ENABPROT3 0x7f /* Enable Sector Protection - byte 3 */
+# define AT45DB_ENABPROT4 0xa9 /* Enable Sector Protection - byte 4 */
+#define AT45DB_DISABPROT1 0x3d /* Disable Sector Protection - byte 1 */
+# define AT45DB_DISABPROT2 0x2a /* Disable Sector Protection - byte 2 */
+# define AT45DB_DISABPROT3 0x7f /* Disable Sector Protection - byte 3 */
+# define AT45DB_DISABPROT4 0x9a /* Disable Sector Protection - byte 4 */
+#define AT45DB_ERASEPROT1 0x3d /* Erase Sector Protection Register - byte 1 */
+# define AT45DB_ERASEPROT2 0x2a /* Erase Sector Protection Register - byte 2 */
+# define AT45DB_ERASEPROT3 0x7f /* Erase Sector Protection Register - byte 3 */
+# define AT45DB_ERASEPROT4 0xcf /* Erase Sector Protection Register - byte 4 */
+#define AT45DB_PROGPROT1 0x3d /* Program Sector Protection Register - byte 1 */
+# define AT45DB_PROGPROT2 0x2a /* Program Sector Protection Register - byte 2 */
+# define AT45DB_PROGPROT3 0x7f /* Program Sector Protection Register - byte 3 */
+# define AT45DB_PROGPROT4 0xfc /* Program Sector Protection Register - byte 4 */
+#define AT45DB_RDPROT 0x32 /* Read Sector Protection Register */
+#define AT45DB_LOCKDOWN1 0x3d /* Sector Lockdown - byte 1 */
+# define AT45DB_LOCKDOWN2 0x2a /* Sector Lockdown - byte 2 */
+# define AT45DB_LOCKDOWN3 0x7f /* Sector Lockdown - byte 3 */
+# define AT45DB_LOCKDOWN4 0x30 /* Sector Lockdown - byte 4 */
+#define AT45DB_RDLOCKDOWN 0x35 /* Read Sector Lockdown Register */
+#define AT45DB_PROGSEC1 0x9b /* Program Security Register - byte 1 */
+# define AT45DB_PROGSEC2 0x00 /* Program Security Register - byte 2 */
+# define AT45DB_PROGSEC3 0x00 /* Program Security Register - byte 3 */
+# define AT45DB_PROGSEC4 0x00 /* Program Security Register - byte 4 */
+#define AT45DB_RDSEC 0x77 /* Read Security Register */
+
+/* Additional commands */
+
+#define AT45DB_MNTOBF1XFR 0x53 /* Main Memory Page to Buffer 1 Transfer */
+#define AT45DB_MNTOBF2XFR 0x55 /* Main Memory Page to Buffer 2 Transfer */
+#define AT45DB_MNBF1CMP 0x60 /* Main Memory Page to Buffer 1 Compare */
+#define AT45DB_MNBF2CMP 0x61 /* Main Memory Page to Buffer 2 Compare */
+#define AT45DB_AUTOWRBF1 0x58 /* Auto Page Rewrite through Buffer 1 */
+#define AT45DB_AUTOWRBF2 0x59 /* Auto Page Rewrite through Buffer 2 */
+#define AT45DB_PWRDOWN 0xb9 /* Deep Power-down */
+#define AT45DB_RESUME 0xab /* Resume from Deep Power-down */
+#define AT45DB_RDSR 0xd7 /* Status Register Read */
+#define AT45DB_RDDEVID 0x9f /* Manufacturer and Device ID Read */
+
+#define AT45DB_MANUFACTURER 0x1f /* Manufacturer ID: Atmel */
+#define AT45DB_DEVID1_CAPMSK 0x1f /* Bits 0-4: Capacity */
+#define AT45DB_DEVID1_1MBIT 0x02 /* xxx0 0010 = 1Mbit AT45DB011 */
+#define AT45DB_DEVID1_2MBIT 0x03 /* xxx0 0012 = 2Mbit AT45DB021 */
+#define AT45DB_DEVID1_4MBIT 0x04 /* xxx0 0100 = 4Mbit AT45DB041 */
+#define AT45DB_DEVID1_8MBIT 0x05 /* xxx0 0101 = 8Mbit AT45DB081 */
+#define AT45DB_DEVID1_16MBIT 0x06 /* xxx0 0110 = 16Mbit AT45DB161 */
+#define AT45DB_DEVID1_32MBIT 0x07 /* xxx0 0111 = 32Mbit AT45DB321 */
+#define AT45DB_DEVID1_64MBIT 0x08 /* xxx0 1000 = 32Mbit AT45DB641 */
+#define AT45DB_DEVID1_FAMMSK 0xe0 /* Bits 5-7: Family */
+#define AT45DB_DEVID1_DFLASH 0x20 /* 001x xxxx = Dataflash */
+#define AT45DB_DEVID1_AT26DF 0x40 /* 010x xxxx = AT26DFxxx series (Not supported) */
+#define AT45DB_DEVID2_VERMSK 0x1f /* Bits 0-4: MLC mask */
+#define AT45DB_DEVID2_MLCMSK 0xe0 /* Bits 5-7: MLC mask */
+
+/* Status register bit definitions */
+
+#define AT45DB_SR_RDY (1 << 7) /* Bit 7: RDY/ Not BUSY */
+#define AT45DB_SR_COMP (1 << 6) /* Bit 6: COMP */
+#define AT45DB_SR_PROTECT (1 << 1) /* Bit 1: PROTECT */
+#define AT45DB_SR_PGSIZE (1 << 0) /* Bit 0: PAGE_SIZE */
+
+/* 1 Block = 16 pages; 1 sector = 256 pages */
+
+#define PG_PER_BLOCK (16)
+#define PG_PER_SECTOR (256)
+
+/************************************************************************************
+ * Private Types
+ ************************************************************************************/
+
+/* This type represents the state of the MTD device. The struct mtd_dev_s
+ * must appear at the beginning of the definition so that you can freely
+ * cast between pointers to struct mtd_dev_s and struct at45db_dev_s.
+ */
+
+struct at45db_dev_s
+{
+ struct mtd_dev_s mtd; /* MTD interface */
+ FAR struct spi_dev_s *spi; /* Saved SPI interface instance */
+ uint8_t pageshift; /* log2 of the page size (eg. 1 << 9 = 512) */
+ uint32_t npages; /* Number of pages in the device */
+};
+
+/************************************************************************************
+ * Private Function Prototypes
+ ************************************************************************************/
+
+/* Lock and per-transaction configuration */
+
+static void at45db_lock(struct at45db_dev_s *priv);
+static inline void at45db_unlock(struct at45db_dev_s *priv);
+
+/* Power management */
+
+#ifdef CONFIG_AT45DB_PWRSAVE
+static void at45db_pwrdown(struct at45db_dev_s *priv);
+static void at45db_resume(struct at45db_dev_s *priv);
+#else
+# define at45db_pwrdown(priv)
+# define at45db_resume(priv)
+#endif
+
+/* Low-level AT45DB Helpers */
+
+static inline int at45db_rdid(struct at45db_dev_s *priv);
+static inline uint8_t at45db_rdsr(struct at45db_dev_s *priv);
+static uint8_t at45db_waitbusy(struct at45db_dev_s *priv);
+static inline void at45db_pgerase(struct at45db_dev_s *priv, off_t offset);
+static inline int at32db_chiperase(struct at45db_dev_s *priv);
+static inline void at45db_pgwrite(struct at45db_dev_s *priv, FAR const uint8_t *buffer,
+ off_t offset);
+
+/* MTD driver methods */
+
+static int at45db_erase(FAR struct mtd_dev_s *mtd, off_t startblock, size_t nblocks);
+static ssize_t at45db_bread(FAR struct mtd_dev_s *mtd, off_t startblock,
+ size_t nblocks, FAR uint8_t *buf);
+static ssize_t at45db_bwrite(FAR struct mtd_dev_s *mtd, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buf);
+static ssize_t at45db_read(FAR struct mtd_dev_s *mtd, off_t offset, size_t nbytes,
+ FAR uint8_t *buffer);
+static int at45db_ioctl(FAR struct mtd_dev_s *mtd, int cmd, unsigned long arg);
+
+/************************************************************************************
+ * Private Data
+ ************************************************************************************/
+
+/* Chip erase sequence */
+
+#define CHIP_ERASE_SIZE 4
+static const uint8_t g_chiperase[CHIP_ERASE_SIZE] = {0xc7, 0x94, 0x80, 0x9a};
+
+/* Sequence to program the device to binary page sizes{256, 512, 1024} */
+
+#define BINPGSIZE_SIZE 4
+static const uint8_t g_binpgsize[BINPGSIZE_SIZE] = {0x3d, 0x2a, 0x80, 0xa6};
+
+/************************************************************************************
+ * Private Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: at45db_lock
+ ************************************************************************************/
+
+static void at45db_lock(struct at45db_dev_s *priv)
+{
+ /* On SPI busses where there are multiple devices, it will be necessary to
+ * lock SPI to have exclusive access to the busses for a sequence of
+ * transfers. The bus should be locked before the chip is selected.
+ *
+ * This is a blocking call and will not return until we have exclusiv access to
+ * the SPI buss. We will retain that exclusive access until the bus is unlocked.
+ */
+
+ (void)SPI_LOCK(priv->spi, true);
+
+ /* After locking the SPI bus, the we also need call the setfrequency, setbits, and
+ * setmode methods to make sure that the SPI is properly configured for the device.
+ * If the SPI buss is being shared, then it may have been left in an incompatible
+ * state.
+ */
+
+ SPI_SETMODE(priv->spi, SPIDEV_MODE0);
+ SPI_SETBITS(priv->spi, 8);
+ (void)SPI_SETFREQUENCY(priv->spi, CONFIG_AT45DB_FREQUENCY);
+}
+
+/************************************************************************************
+ * Name: at45db_unlock
+ ************************************************************************************/
+
+static inline void at45db_unlock(struct at45db_dev_s *priv)
+{
+ (void)SPI_LOCK(priv->spi, false);
+}
+
+/************************************************************************************
+ * Name: at45db_pwrdown
+ ************************************************************************************/
+
+#ifdef CONFIG_AT45DB_PWRSAVE
+static void at45db_pwrdown(struct at45db_dev_s *priv)
+{
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
+ SPI_SEND(priv->spi, AT45DB_PWRDOWN);
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
+}
+#endif
+
+/************************************************************************************
+ * Name: at45db_resume
+ ************************************************************************************/
+
+#ifdef CONFIG_AT45DB_PWRSAVE
+static void at45db_resume(struct at45db_dev_s *priv)
+{
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
+ SPI_SEND(priv->spi, AT45DB_RESUME);
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
+ up_udelay(50);
+}
+#endif
+
+/************************************************************************************
+ * Name: at45db_rdid
+ ************************************************************************************/
+
+static inline int at45db_rdid(struct at45db_dev_s *priv)
+{
+ uint8_t capacity;
+ uint8_t devid[3];
+
+ fvdbg("priv: %p\n", priv);
+
+ /* Configure the bus, and select this FLASH part. (The caller should alread have
+ * loced the bus for exclusive access)
+ */
+
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
+
+ /* Send the " Manufacturer and Device ID Read" command and read the next three
+ * ID bytes from the FLASH.
+ */
+
+ (void)SPI_SEND(priv->spi, AT45DB_RDDEVID);
+ SPI_RECVBLOCK(priv->spi, devid, 3);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
+
+ fvdbg("manufacturer: %02x devid1: %02x devid2: %02x\n",
+ devid[0], devid[1], devid[2]);
+
+ /* Check for a valid manufacturer and memory family */
+
+ if (devid[0] == AT45DB_MANUFACTURER &&
+ (devid[1] & AT45DB_DEVID1_FAMMSK) == AT45DB_DEVID1_DFLASH)
+ {
+ /* Okay.. is it a FLASH capacity that we understand? */
+
+ capacity = devid[1] & AT45DB_DEVID1_CAPMSK;
+ switch (capacity)
+ {
+ case AT45DB_DEVID1_1MBIT:
+ /* Save the FLASH geometry for the 16Mbit AT45DB011 */
+
+ priv->pageshift = 8; /* Page size = 256 bytes */
+ priv->npages = 512; /* 512 pages */
+ return OK;
+
+ case AT45DB_DEVID1_2MBIT:
+ /* Save the FLASH geometry for the 16Mbit AT45DB021 */
+
+ priv->pageshift = 8; /* Page size = 256/264 bytes */
+ priv->npages = 1024; /* 1024 pages */
+ return OK;
+
+ case AT45DB_DEVID1_4MBIT:
+ /* Save the FLASH geometry for the 16Mbit AT45DB041 */
+
+ priv->pageshift = 8; /* Page size = 256/264 bytes */
+ priv->npages = 2048; /* 2048 pages */
+ return OK;
+
+ case AT45DB_DEVID1_8MBIT:
+ /* Save the FLASH geometry for the 16Mbit AT45DB081 */
+
+ priv->pageshift = 8; /* Page size = 256/264 bytes */
+ priv->npages = 4096; /* 4096 pages */
+ return OK;
+
+ case AT45DB_DEVID1_16MBIT:
+ /* Save the FLASH geometry for the 16Mbit AT45DB161 */
+
+ priv->pageshift = 9; /* Page size = 512/528 bytes */
+ priv->npages = 4096; /* 4096 pages */
+ return OK;
+
+ case AT45DB_DEVID1_32MBIT:
+ /* Save the FLASH geometry for the 16Mbit AT45DB321 */
+
+ priv->pageshift = 9; /* Page size = 512/528 bytes */
+ priv->npages = 8192; /* 8192 pages */
+ return OK;
+
+ case AT45DB_DEVID1_64MBIT:
+ /* Save the FLASH geometry for the 16Mbit AT45DB321 */
+
+ priv->pageshift = 10; /* Page size = 1024/1056 bytes */
+ priv->npages = 8192; /* 8192 pages */
+ return OK;
+
+ default:
+ return -ENODEV;
+ }
+ }
+
+ return -ENODEV;
+}
+
+/************************************************************************************
+ * Name: at45db_rdsr
+ ************************************************************************************/
+
+static inline uint8_t at45db_rdsr(struct at45db_dev_s *priv)
+{
+ uint8_t retval;
+
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
+ SPI_SEND(priv->spi, AT45DB_RDSR);
+ retval = SPI_SEND(priv->spi, 0xff);
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
+ return retval;
+}
+
+/************************************************************************************
+ * Name: at45db_waitbusy
+ ************************************************************************************/
+
+static uint8_t at45db_waitbusy(struct at45db_dev_s *priv)
+{
+ uint8_t sr;
+
+ /* Poll the device, waiting for it to report that it is ready */
+
+ do
+ {
+ up_udelay(10);
+ sr = (uint8_t)at45db_rdsr(priv);
+ }
+ while ((sr & AT45DB_SR_RDY) == 0);
+ return sr;
+}
+
+/************************************************************************************
+ * Name: at45db_pgerase
+ ************************************************************************************/
+
+static inline void at45db_pgerase(struct at45db_dev_s *priv, off_t sector)
+{
+ uint8_t erasecmd[4];
+ off_t offset = sector << priv->pageshift;
+
+ fvdbg("sector: %08lx\n", (long)sector);
+
+ /* Higher performance write logic: We leave the chip busy after write and erase
+ * operations. This improves write and erase performance because we do not have
+ * to wait as long between transactions (other processing can occur while the chip
+ * is busy) but means that the chip must stay powered and that we must check if
+ * the chip is still busy on each entry point.
+ */
+
+#ifdef CONFIG_AT45DB_PREWAIT
+ at45db_waitbusy(priv);
+#endif
+
+ /* "The Page Erase command can be used to individually erase any page in the main
+ * memory array allowing the Buffer to Main Memory Page Program to be utilized at a
+ * later time. ... To perform a page erase in the binary page size ..., the
+ * opcode 81H must be loaded into the device, followed by three address bytes
+ * ... When a low-to-high transition occurs on the CS pin, the part will erase the
+ * selected page (the erased state is a logical 1). ... the status register and the
+ * RDY/BUSY pin will indicate that the part is busy."
+ */
+
+ erasecmd[0] = AT45DB_PGERASE; /* Page erase command */
+ erasecmd[1] = (offset >> 16) & 0xff; /* 24-bit offset MS bytes */
+ erasecmd[2] = (offset >> 8) & 0xff; /* 24-bit offset middle bytes */
+ erasecmd[3] = offset & 0xff; /* 24-bit offset LS bytes */
+
+ /* Erase the page */
+
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
+ SPI_SNDBLOCK(priv->spi, erasecmd, 4);
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
+
+ /* Wait for any erase to complete if we are not trying to improve write
+ * performance. (see comments above).
+ */
+
+#ifndef CONFIG_AT45DB_PREWAIT
+ at45db_waitbusy(priv);
+#endif
+ fvdbg("Erased\n");
+}
+
+/************************************************************************************
+ * Name: at32db_chiperase
+ ************************************************************************************/
+
+static inline int at32db_chiperase(struct at45db_dev_s *priv)
+{
+ fvdbg("priv: %p\n", priv);
+
+ /* Higher performance write logic: We leave the chip busy after write and erase
+ * operations. This improves write and erase performance because we do not have
+ * to wait as long between transactions (other processing can occur while the chip
+ * is busy) but means that the chip must stay powered and that we must check if
+ * the chip is still busy on each entry point.
+ */
+
+#ifdef CONFIG_AT45DB_PREWAIT
+ at45db_waitbusy(priv);
+#endif
+
+ /* "The entire main memory can be erased at one time by using the Chip Erase
+ * command. To execute the Chip Erase command, a 4-byte command sequence C7H, 94H,
+ * 80H and 9AH must be clocked into the device. ... After the last bit of the opcode
+ * sequence has been clocked in, the CS pin can be deasserted to start the erase
+ * process. ... the Status Register will indicate that the device is busy. The Chip
+ * Erase command will not affect sectors that are protected or locked down...
+ */
+
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
+ SPI_SNDBLOCK(priv->spi, g_chiperase, CHIP_ERASE_SIZE);
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
+
+ /* Wait for any erase to complete if we are not trying to improve write
+ * performance. (see comments above).
+ */
+
+#ifndef CONFIG_AT45DB_PREWAIT
+ at45db_waitbusy(priv);
+#endif
+ return OK;
+}
+
+/************************************************************************************
+ * Name: at45db_pgwrite
+ ************************************************************************************/
+
+static inline void at45db_pgwrite(struct at45db_dev_s *priv, FAR const uint8_t *buffer,
+ off_t page)
+{
+ uint8_t wrcmd [4];
+ off_t offset = page << priv->pageshift;
+
+ fvdbg("page: %08lx offset: %08lx\n", (long)page, (long)offset);
+
+ /* We assume that sectors are not write protected */
+
+ wrcmd[0] = AT45DB_MNTHRUBF1; /* To main memory through buffer 1 */
+ wrcmd[1] = (offset >> 16) & 0xff; /* 24-bit address MS byte */
+ wrcmd[2] = (offset >> 8) & 0xff; /* 24-bit address middle byte */
+ wrcmd[3] = offset & 0xff; /* 24-bit address LS byte */
+
+ /* Higher performance write logic: We leave the chip busy after write and erase
+ * operations. This improves write and erase performance because we do not have
+ * to wait as long between transactions (other processing can occur while the chip
+ * is busy) but means that the chip must stay powered and that we must check if
+ * the chip is still busy on each entry point.
+ */
+
+#ifdef CONFIG_AT45DB_PREWAIT
+ at45db_waitbusy(priv);
+#endif
+
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
+ SPI_SNDBLOCK(priv->spi, wrcmd, 4);
+ SPI_SNDBLOCK(priv->spi, buffer, 1 << priv->pageshift);
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
+
+ /* Wait for any erase to complete if we are not trying to improve write
+ * performance. (see comments above).
+ */
+
+#ifndef CONFIG_AT45DB_PREWAIT
+ at45db_waitbusy(priv);
+#endif
+ fvdbg("Written\n");
+}
+
+/************************************************************************************
+ * Name: at45db_erase
+ ************************************************************************************/
+
+static int at45db_erase(FAR struct mtd_dev_s *mtd, off_t startblock, size_t nblocks)
+{
+ FAR struct at45db_dev_s *priv = (FAR struct at45db_dev_s *)mtd;
+ size_t pgsleft = nblocks;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* Take the lock so that we have exclusive access to the bus, then power up the
+ * FLASH device.
+ */
+
+ at45db_lock(priv);
+ at45db_resume(priv);
+
+ /* Then erase each page */
+
+ while (pgsleft-- > 0)
+ {
+ /* Erase each sector */
+
+ at45db_pgerase(priv, startblock);
+ startblock++;
+ }
+
+ at45db_pwrdown(priv);
+ at45db_unlock(priv);
+ return (int)nblocks;
+}
+
+/************************************************************************************
+ * Name: at45db_bread
+ ************************************************************************************/
+
+static ssize_t at45db_bread(FAR struct mtd_dev_s *mtd, off_t startblock, size_t nblocks,
+ FAR uint8_t *buffer)
+{
+ FAR struct at45db_dev_s *priv = (FAR struct at45db_dev_s *)mtd;
+ ssize_t nbytes;
+
+ /* On this device, we can handle the block read just like the byte-oriented read */
+
+ nbytes = at45db_read(mtd, startblock << priv->pageshift, nblocks << priv->pageshift, buffer);
+ if (nbytes > 0)
+ {
+ return nbytes >> priv->pageshift;
+ }
+ return nbytes;
+}
+
+/************************************************************************************
+ * Name: at45db_bwrite
+ ************************************************************************************/
+
+static ssize_t at45db_bwrite(FAR struct mtd_dev_s *mtd, off_t startblock, size_t nblocks,
+ FAR const uint8_t *buffer)
+{
+ FAR struct at45db_dev_s *priv = (FAR struct at45db_dev_s *)mtd;
+ size_t pgsleft = nblocks;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* Take the lock so that we have exclusive access to the bus, then power up the
+ * FLASH device.
+ */
+
+ at45db_lock(priv);
+ at45db_resume(priv);
+
+ /* Write each page to FLASH */
+
+ while (pgsleft-- > 0)
+ {
+ at45db_pgwrite(priv, buffer, startblock);
+ startblock++;
+ }
+
+ at45db_pwrdown(priv);
+ at45db_unlock(priv);
+
+ return nblocks;
+}
+
+/************************************************************************************
+ * Name: at45db_read
+ ************************************************************************************/
+
+static ssize_t at45db_read(FAR struct mtd_dev_s *mtd, off_t offset, size_t nbytes,
+ FAR uint8_t *buffer)
+{
+ FAR struct at45db_dev_s *priv = (FAR struct at45db_dev_s *)mtd;
+ uint8_t rdcmd [5];
+
+ fvdbg("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
+
+ /* Set up for the read */
+
+ rdcmd[0] = AT45DB_RDARRAYHF; /* FAST_READ is safe at all supported SPI speeds. */
+ rdcmd[1] = (offset >> 16) & 0xff; /* 24-bit address upper byte */
+ rdcmd[2] = (offset >> 8) & 0xff; /* 24-bit address middle byte */
+ rdcmd[3] = offset & 0xff; /* 24-bit address least significant byte */
+ rdcmd[4] = 0; /* Dummy byte */
+
+ /* Take the lock so that we have exclusive access to the bus, then power up the
+ * FLASH device.
+ */
+
+ at45db_lock(priv);
+ at45db_resume(priv);
+
+ /* Higher performance write logic: We leave the chip busy after write and erase
+ * operations. This improves write and erase performance because we do not have
+ * to wait as long between transactions (other processing can occur while the chip
+ * is busy) but means that the chip must stay powered and that we must check if
+ * the chip is still busy on each entry point.
+ */
+
+#ifdef CONFIG_AT45DB_PREWAIT
+ at45db_waitbusy(priv);
+#endif
+
+ /* Perform the read */
+
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
+ SPI_SNDBLOCK(priv->spi, rdcmd, 5);
+ SPI_RECVBLOCK(priv->spi, buffer, nbytes);
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
+
+ at45db_pwrdown(priv);
+ at45db_unlock(priv);
+
+ fvdbg("return nbytes: %d\n", (int)nbytes);
+ return nbytes;
+}
+
+/************************************************************************************
+ * Name: at45db_ioctl
+ ************************************************************************************/
+
+static int at45db_ioctl(FAR struct mtd_dev_s *mtd, int cmd, unsigned long arg)
+{
+ FAR struct at45db_dev_s *priv = (FAR struct at45db_dev_s *)mtd;
+ int ret = -EINVAL; /* Assume good command with bad parameters */
+
+ fvdbg("cmd: %d \n", cmd);
+
+ switch (cmd)
+ {
+ case MTDIOC_GEOMETRY:
+ {
+ FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
+ if (geo)
+ {
+ /* Populate the geometry structure with information need to know
+ * the capacity and how to access the device.
+ *
+ * NOTE: that the device is treated as though it where just an array
+ * of fixed size blocks. That is most likely not true, but the client
+ * will expect the device logic to do whatever is necessary to make it
+ * appear so.
+ */
+
+ geo->blocksize = (1 << priv->pageshift);
+ geo->erasesize = geo->blocksize;
+ geo->neraseblocks = priv->npages;
+ ret = OK;
+
+ fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
+ geo->blocksize, geo->erasesize, geo->neraseblocks);
+ }
+ }
+ break;
+
+ case MTDIOC_BULKERASE:
+ {
+ /* Take the lock so that we have exclusive access to the bus, then
+ * power up the FLASH device.
+ */
+
+ at45db_lock(priv);
+ at45db_resume(priv);
+
+ /* Erase the entire device */
+
+ ret = at32db_chiperase(priv);
+ at45db_pwrdown(priv);
+ at45db_unlock(priv);
+ }
+ break;
+
+ case MTDIOC_XIPBASE:
+ default:
+ ret = -ENOTTY; /* Bad command */
+ break;
+ }
+
+ fvdbg("return %d\n", ret);
+ return ret;
+}
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: at45db_initialize
+ *
+ * Description:
+ * Create an initialize MTD device instance. MTD devices are not registered
+ * in the file system, but are created as instances that can be bound to
+ * other functions (such as a block or character driver front end).
+ *
+ ************************************************************************************/
+
+FAR struct mtd_dev_s *at45db_initialize(FAR struct spi_dev_s *spi)
+{
+ FAR struct at45db_dev_s *priv;
+ uint8_t sr;
+ int ret;
+
+ fvdbg("spi: %p\n", spi);
+
+ /* Allocate a state structure (we allocate the structure instead of using
+ * a fixed, static allocation so that we can handle multiple FLASH devices.
+ * The current implementation would handle only one FLASH part per SPI
+ * device (only because of the SPIDEV_FLASH definition) and so would have
+ * to be extended to handle multiple FLASH parts on the same SPI bus.
+ */
+
+ priv = (FAR struct at45db_dev_s *)kmalloc(sizeof(struct at45db_dev_s));
+ if (priv)
+ {
+ /* Initialize the allocated structure */
+
+ priv->mtd.erase = at45db_erase;
+ priv->mtd.bread = at45db_bread;
+ priv->mtd.bwrite = at45db_bwrite;
+ priv->mtd.read = at45db_read;
+ priv->mtd.ioctl = at45db_ioctl;
+ priv->spi = spi;
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(spi, SPIDEV_FLASH, false);
+
+ /* Lock and configure the SPI bus. */
+
+ at45db_lock(priv);
+ at45db_resume(priv);
+
+ /* Identify the FLASH chip and get its capacity */
+
+ ret = at45db_rdid(priv);
+ if (ret != OK)
+ {
+ /* Unrecognized! Discard all of that work we just did and return NULL */
+
+ fdbg("Unrecognized\n");
+ goto errout;
+ }
+
+ /* Get the value of the status register (as soon as the device is ready) */
+
+ sr = at45db_waitbusy(priv);
+
+ /* Check if the device is configured as 256, 512 or 1024 bytes-per-page device */
+
+ if ((sr & AT45DB_SR_PGSIZE) == 0)
+ {
+ /* No, re-program it for the binary page size. NOTE: A power cycle
+ * is required after the device has be re-programmed.
+ */
+
+ fdbg("Reprogramming page size\n");
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, true);
+ SPI_SNDBLOCK(priv->spi, g_binpgsize, BINPGSIZE_SIZE);
+ SPI_SELECT(priv->spi, SPIDEV_FLASH, false);
+ goto errout;
+ }
+
+ /* Release the lock and power down the device */
+
+ at45db_pwrdown(priv);
+ at45db_unlock(priv);
+ }
+
+ fvdbg("Return %p\n", priv);
+ return (FAR struct mtd_dev_s *)priv;
+
+/* On any failure, we need free memory allocations and release the lock that
+ * we hold on the SPI bus. On failures, assume that we cannot talk to the
+ * device to do any more.
+ */
+
+errout:
+ at45db_unlock(priv);
+ kfree(priv);
+ return NULL;
+}
diff --git a/nuttx/drivers/mtd/flash_eraseall.c b/nuttx/drivers/mtd/flash_eraseall.c
new file mode 100644
index 000000000..ce0cfe649
--- /dev/null
+++ b/nuttx/drivers/mtd/flash_eraseall.c
@@ -0,0 +1,117 @@
+/****************************************************************************
+ * drivers/mtd/flash_eraseall.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd.h>
+
+/****************************************************************************
+ * Private Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: flash_eraseall
+ *
+ * Description:
+ * Call a block driver with the MDIOC_BULKERASE ioctl command. This will
+ * cause the MTD driver to erase all of the flash.
+ *
+ ****************************************************************************/
+
+int flash_eraseall(FAR const char *driver)
+{
+ FAR struct inode *inode;
+ FAR const struct block_operations *ops;
+ int ret;
+
+ /* Open the block driver */
+
+ ret = open_blockdriver(driver ,0, &inode);
+ if (ret < 0)
+ {
+ fdbg("ERROR: Failed to open '%s': %d\n", driver, ret);
+ return ret;
+ }
+
+ /* Get the block operations */
+
+ ops = inode->u.i_bops;
+
+ /* Invoke the block driver ioctl method */
+
+ ret = -EPERM;
+ if (ops->ioctl)
+ {
+ ret = ops->ioctl(inode, MTDIOC_BULKERASE, 0);
+ if (ret < 0)
+ {
+ fdbg("ERROR: MTD ioctl(%04x) failed: %d\n", MTDIOC_BULKERASE, ret);
+ }
+ }
+
+ /* Close the block driver */
+
+ close_blockdriver(inode);
+ return ret;
+}
diff --git a/nuttx/drivers/mtd/ftl.c b/nuttx/drivers/mtd/ftl.c
new file mode 100644
index 000000000..cdb35aa5c
--- /dev/null
+++ b/nuttx/drivers/mtd/ftl.c
@@ -0,0 +1,578 @@
+/****************************************************************************
+ * drivers/mtd/ftl.c
+ *
+ * Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd.h>
+#include <nuttx/rwbuffer.h>
+
+/****************************************************************************
+ * Private Definitions
+ ****************************************************************************/
+
+#if defined(CONFIG_FS_READAHEAD) || (defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER))
+# defined CONFIG_FTL_RWBUFFER 1
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ftl_struct_s
+{
+ FAR struct mtd_dev_s *mtd; /* Contained MTD interface */
+ struct mtd_geometry_s geo; /* Device geometry */
+#ifdef CONFIG_FTL_RWBUFFER
+ struct rwbuffer_s rwb; /* Read-ahead/write buffer support */
+#endif
+ uint16_t blkper; /* R/W blocks per erase block */
+#ifdef CONFIG_FS_WRITABLE
+ FAR uint8_t *eblock; /* One, in-memory erase block */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int ftl_open(FAR struct inode *inode);
+static int ftl_close(FAR struct inode *inode);
+static ssize_t ftl_reload(FAR void *priv, FAR uint8_t *buffer,
+ off_t startblock, size_t nblocks);
+static ssize_t ftl_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors);
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t ftl_flush(FAR void *priv, FAR const uint8_t *buffer,
+ off_t startblock, size_t nblocks);
+static ssize_t ftl_write(FAR struct inode *inode, const unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors);
+#endif
+static int ftl_geometry(FAR struct inode *inode, struct geometry *geometry);
+static int ftl_ioctl(FAR struct inode *inode, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct block_operations g_bops =
+{
+ ftl_open, /* open */
+ ftl_close, /* close */
+ ftl_read, /* read */
+#ifdef CONFIG_FS_WRITABLE
+ ftl_write, /* write */
+#else
+ NULL, /* write */
+#endif
+ ftl_geometry, /* geometry */
+ ftl_ioctl /* ioctl */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ftl_open
+ *
+ * Description: Open the block device
+ *
+ ****************************************************************************/
+
+static int ftl_open(FAR struct inode *inode)
+{
+ fvdbg("Entry\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: ftl_close
+ *
+ * Description: close the block device
+ *
+ ****************************************************************************/
+
+static int ftl_close(FAR struct inode *inode)
+{
+ fvdbg("Entry\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: ftl_reload
+ *
+ * Description: Read the specified numer of sectors
+ *
+ ****************************************************************************/
+
+static ssize_t ftl_reload(FAR void *priv, FAR uint8_t *buffer,
+ off_t startblock, size_t nblocks)
+{
+ struct ftl_struct_s *dev = (struct ftl_struct_s *)priv;
+ ssize_t nread;
+
+ /* Read the full erase block into the buffer */
+
+ nread = MTD_BREAD(dev->mtd, startblock, nblocks, buffer);
+ if (nread != nblocks)
+ {
+ fdbg("Read %d blocks starting at block %d failed: %d\n",
+ nblocks, startblock, nread);
+ }
+ return nread;
+}
+
+/****************************************************************************
+ * Name: ftl_read
+ *
+ * Description: Read the specified numer of sectors
+ *
+ ****************************************************************************/
+
+static ssize_t ftl_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
+{
+ struct ftl_struct_s *dev;
+
+ fvdbg("sector: %d nsectors: %d\n", start_sector, nsectors);
+
+ DEBUGASSERT(inode && inode->i_private);
+ dev = (struct ftl_struct_s *)inode->i_private;
+#ifdef CONFIG_FS_READAHEAD
+ return rwb_read(&dev->rwb, start_sector, nsectors, buffer);
+#else
+ return ftl_reload(dev, buffer, start_sector, nsectors);
+#endif
+}
+
+/****************************************************************************
+ * Name: ftl_flush
+ *
+ * Description: Write the specified number of sectors
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t ftl_flush(FAR void *priv, FAR const uint8_t *buffer,
+ off_t startblock, size_t nblocks)
+{
+ struct ftl_struct_s *dev = (struct ftl_struct_s *)priv;
+ off_t alignedblock;
+ off_t mask;
+ off_t rwblock;
+ off_t eraseblock;
+ off_t offset;
+ size_t remaining;
+ size_t nxfrd;
+ int nbytes;
+ int ret;
+
+ /* Get the aligned block. Here is is assumed: (1) The number of R/W blocks
+ * per erase block is a power of 2, and (2) the erase begins with that same
+ * alignment.
+ */
+
+ mask = dev->blkper - 1;
+ alignedblock = (startblock + mask) & ~mask;
+
+ /* Handle partial erase blocks before the first unaligned block */
+
+ remaining = nblocks;
+ if (alignedblock > startblock)
+ {
+ /* Read the full erase block into the buffer */
+
+ rwblock = startblock & ~mask;
+ nxfrd = MTD_BREAD(dev->mtd, rwblock, dev->blkper, dev->eblock);
+ if (nxfrd != dev->blkper)
+ {
+ fdbg("Read erase block %d failed: %d\n", rwblock, nxfrd);
+ return -EIO;
+ }
+
+ /* Then erase the erase block */
+
+ eraseblock = rwblock / dev->blkper;
+ ret = MTD_ERASE(dev->mtd, eraseblock, 1);
+ if (ret < 0)
+ {
+ fdbg("Erase block=%d failed: %d\n", eraseblock, ret);
+ return ret;
+ }
+
+ /* Copy the user data at the end of the buffered erase block */
+
+ offset = (startblock & mask) * dev->geo.blocksize;
+ nbytes = dev->geo.erasesize - offset;
+ fvdbg("Copy %d bytes into erase block=%d at offset=%d\n",
+ nbytes, eraseblock, offset);
+ memcpy(dev->eblock + offset, buffer, nbytes);
+
+ /* And write the erase back to flash */
+
+ nxfrd = MTD_BWRITE(dev->mtd, rwblock, dev->blkper, dev->eblock);
+ if (nxfrd != dev->blkper)
+ {
+ fdbg("Write erase block %d failed: %d\n", rwblock, nxfrd);
+ return -EIO;
+ }
+
+ /* Then update for amount written */
+
+ remaining -= dev->blkper - (startblock & mask);
+ buffer += nbytes;
+ }
+
+ /* How handle full erase pages in the middle */
+
+ while (remaining >= dev->blkper)
+ {
+ /* Erase the erase block */
+
+ eraseblock = alignedblock / dev->blkper;
+ ret = MTD_ERASE(dev->mtd, eraseblock, 1);
+ if (ret < 0)
+ {
+ fdbg("Erase block=%d failed: %d\n", eraseblock, ret);
+ return ret;
+ }
+
+ /* Write a full erase back to flash */
+
+ fvdbg("Write %d bytes into erase block=%d at offset=0\n",
+ dev->geo.erasesize, alignedblock);
+ nxfrd = MTD_BWRITE(dev->mtd, alignedblock, dev->blkper, buffer);
+ if (nxfrd != dev->blkper)
+ {
+ fdbg("Write erase block %d failed: %d\n", alignedblock, nxfrd);
+ return -EIO;
+ }
+
+ /* Then update for amount written */
+
+ alignedblock += dev->blkper;
+ remaining -= dev->blkper;
+ buffer += dev->geo.erasesize;
+ }
+
+ /* Finally, handler any partial blocks after the last full erase block */
+
+ if (remaining > 0)
+ {
+ /* Read the full erase block into the buffer */
+
+ nxfrd = MTD_BREAD(dev->mtd, alignedblock, dev->blkper, dev->eblock);
+ if (nxfrd != dev->blkper)
+ {
+ fdbg("Read erase block %d failed: %d\n", alignedblock, nxfrd);
+ return -EIO;
+ }
+
+ /* Then erase the erase block */
+
+ eraseblock = alignedblock / dev->blkper;
+ ret = MTD_ERASE(dev->mtd, eraseblock, 1);
+ if (ret < 0)
+ {
+ fdbg("Erase block=%d failed: %d\n", eraseblock, ret);
+ return ret;
+ }
+
+ /* Copy the user data at the beginning the buffered erase block */
+
+ nbytes = remaining * dev->geo.blocksize;
+ fvdbg("Copy %d bytes into erase block=%d at offset=0\n",
+ nbytes, alignedblock);
+ memcpy(dev->eblock, buffer, nbytes);
+
+ /* And write the erase back to flash */
+
+ nxfrd = MTD_BWRITE(dev->mtd, alignedblock, dev->blkper, dev->eblock);
+ if (nxfrd != dev->blkper)
+ {
+ fdbg("Write erase block %d failed: %d\n", alignedblock, nxfrd);
+ return -EIO;
+ }
+ }
+
+ return nblocks;
+}
+#endif
+
+/****************************************************************************
+ * Name: ftl_write
+ *
+ * Description: Write (or buffer) the specified number of sectors
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t ftl_write(FAR struct inode *inode, const unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
+{
+ struct ftl_struct_s *dev;
+
+ fvdbg("sector: %d nsectors: %d\n", start_sector, nsectors);
+
+ DEBUGASSERT(inode && inode->i_private);
+ dev = (struct ftl_struct_s *)inode->i_private;
+#ifdef CONFIG_FS_WRITEBUFFER
+ return rwb_write(&dev->rwb, start_sector, nsectors, buffer);
+#else
+ return ftl_flush(dev, buffer, start_sector, nsectors);
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: ftl_geometry
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int ftl_geometry(FAR struct inode *inode, struct geometry *geometry)
+{
+ struct ftl_struct_s *dev;
+
+ fvdbg("Entry\n");
+
+ DEBUGASSERT(inode);
+ if (geometry)
+ {
+ dev = (struct ftl_struct_s *)inode->i_private;
+ geometry->geo_available = true;
+ geometry->geo_mediachanged = false;
+#ifdef CONFIG_FS_WRITABLE
+ geometry->geo_writeenabled = true;
+#else
+ geometry->geo_writeenabled = false;
+#endif
+ geometry->geo_nsectors = dev->geo.neraseblocks * dev->blkper;
+ geometry->geo_sectorsize = dev->geo.blocksize;
+
+ fvdbg("available: true mediachanged: false writeenabled: %s\n",
+ geometry->geo_writeenabled ? "true" : "false");
+ fvdbg("nsectors: %d sectorsize: %d\n",
+ geometry->geo_nsectors, geometry->geo_sectorsize);
+
+ return OK;
+ }
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: ftl_ioctl
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int ftl_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
+{
+ struct ftl_struct_s *dev ;
+ int ret;
+
+ fvdbg("Entry\n");
+ DEBUGASSERT(inode && inode->i_private);
+
+ /* Only one block driver ioctl command is supported by this driver (and
+ * that command is just passed on to the MTD driver in a slightly
+ * different form).
+ */
+
+ if (cmd == BIOC_XIPBASE)
+ {
+ /* The argument accompanying the BIOC_XIPBASE should be non-NULL. If
+ * DEBUG is enabled, we will catch it here instead of in the MTD
+ * driver.
+ */
+
+#ifdef CONFIG_DEBUG
+ if (arg == 0)
+ {
+ fdbg("ERROR: BIOC_XIPBASE argument is NULL\n");
+ return -EINVAL;
+ }
+#endif
+
+ /* Just change the BIOC_XIPBASE command to the MTDIOC_XIPBASE command. */
+
+ cmd = MTDIOC_XIPBASE;
+ }
+
+ /* No other block driver ioctl commmands are not recognized by this
+ * driver. Other possible MTD driver ioctl commands are passed through
+ * to the MTD driver (unchanged).
+ */
+
+ dev = (struct ftl_struct_s *)inode->i_private;
+ ret = MTD_IOCTL(dev->mtd, cmd, arg);
+ if (ret < 0)
+ {
+ fdbg("ERROR: MTD ioctl(%04x) failed: %d\n", cmd, ret);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ftl_initialize
+ *
+ * Description:
+ * Initialize to provide a block driver wrapper around an MTD interface
+ *
+ * Input Parameters:
+ * minor - The minor device number. The MTD block device will be
+ * registered as as /dev/mtdblockN where N is the minor number.
+ * mtd - The MTD device that supports the FLASH interface.
+ *
+ ****************************************************************************/
+
+int ftl_initialize(int minor, FAR struct mtd_dev_s *mtd)
+{
+ struct ftl_struct_s *dev;
+ char devname[16];
+ int ret = -ENOMEM;
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (minor < 0 || minor > 255 || !mtd)
+ {
+ return -EINVAL;
+ }
+#endif
+
+ /* Allocate a FTL device structure */
+
+ dev = (struct ftl_struct_s *)kmalloc(sizeof(struct ftl_struct_s));
+ if (dev)
+ {
+ /* Initialize the FTL device structure */
+
+ dev->mtd = mtd;
+
+ /* Get the device geometry. (casting to uintptr_t first eliminates
+ * complaints on some architectures where the sizeof long is different
+ * from the size of a pointer).
+ */
+
+ ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&dev->geo));
+ if (ret < 0)
+ {
+ fdbg("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret);
+ kfree(dev);
+ return ret;
+ }
+
+ /* Allocate one, in-memory erase block buffer */
+
+#ifdef CONFIG_FS_WRITABLE
+ dev->eblock = (FAR uint8_t *)kmalloc(dev->geo.erasesize);
+ if (!dev->eblock)
+ {
+ fdbg("Failed to allocate an erase block buffer\n");
+ kfree(dev);
+ return -ENOMEM;
+ }
+#endif
+
+ /* Get the number of R/W blocks per erase block */
+
+ dev->blkper = dev->geo.erasesize / dev->geo.blocksize;
+ DEBUGASSERT(dev->blkper * dev->geo.blocksize == dev->geo.erasesize);
+
+ /* Configure read-ahead/write buffering */
+
+#ifdef CONFIG_FTL_RWBUFFER
+ dev->rwb.blocksize = dev->geo.blocksize;
+ dev->rwb.nblocks = dev->geo.neraseblocks * dev->blkper;
+ dev->rwb.dev = (FAR void *)dev;
+
+#if defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER)
+ dev->rwb.wrmaxblocks = dev->blkper;
+ dev->rwb.wrflush = ftl_flush;
+#endif
+
+#ifdef CONFIG_FS_READAHEAD
+ dev->rwb.rhmaxblocks = dev->blkper;
+ dev->rwb.rhreload = ftl_reload;
+#endif
+ ret = rwb_initialize(&dev->rwb);
+ if (ret < 0)
+ {
+ fdbg("rwb_initialize failed: %d\n", ret);
+ kfree(dev);
+ return ret;
+ }
+#endif
+
+ /* Create a MTD block device name */
+
+ snprintf(devname, 16, "/dev/mtdblock%d", minor);
+
+ /* Inode private data is a reference to the FTL device structure */
+
+ ret = register_blockdriver(devname, &g_bops, 0, dev);
+ if (ret < 0)
+ {
+ fdbg("register_blockdriver failed: %d\n", -ret);
+ kfree(dev);
+ }
+ }
+ return ret;
+}
diff --git a/nuttx/drivers/mtd/m25px.c b/nuttx/drivers/mtd/m25px.c
new file mode 100644
index 000000000..4f96c5a3b
--- /dev/null
+++ b/nuttx/drivers/mtd/m25px.c
@@ -0,0 +1,798 @@
+/************************************************************************************
+ * drivers/mtd/m25px.c
+ * Driver for SPI-based M25P1 (128Kbit), M25P64 (32Mbit), M25P64 (64Mbit), and
+ * M25P128 (128Mbit) FLASH (and compatible).
+ *
+ * Copyright (C) 2009-2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ************************************************************************************/
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/spi.h>
+#include <nuttx/mtd.h>
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+/* Configuration ********************************************************************/
+/* Per the data sheet, MP25P10 parts can be driven with either SPI mode 0 (CPOL=0 and
+ * CPHA=0) or mode 3 (CPOL=1 and CPHA=1). But I have heard that other devices can
+ * operated in mode 0 or 1. So you may need to specify CONFIG_MP25P_SPIMODE to
+ * select the best mode for your device. If CONFIG_MP25P_SPIMODE is not defined,
+ * mode 0 will be used.
+ */
+
+#ifndef CONFIG_MP25P_SPIMODE
+# define CONFIG_MP25P_SPIMODE SPIDEV_MODE0
+#endif
+
+/* Various manufacturers may have produced the parts. 0x20 is the manufacturer ID
+ * for the STMicro MP25x serial FLASH. If, for example, you are using the a Macronix
+ * International MX25 serial FLASH, the correct manufacturer ID would be 0xc2.
+ */
+
+#ifndef CONFIG_MP25P_MANUFACTURER
+# define CONFIG_MP25P_MANUFACTURER 0x20
+#endif
+
+/* M25P Registers *******************************************************************/
+/* Indentification register values */
+
+#define M25P_MANUFACTURER CONFIG_MP25P_MANUFACTURER
+#define M25P_MEMORY_TYPE 0x20
+#define M25P_M25P1_CAPACITY 0x11 /* 1 M-bit */
+#define M25P_M25P32_CAPACITY 0x16 /* 32 M-bit */
+#define M25P_M25P64_CAPACITY 0x17 /* 64 M-bit */
+#define M25P_M25P128_CAPACITY 0x18 /* 128 M-bit */
+
+/* M25P1 capacity is 131,072 bytes:
+ * (4 sectors) * (32,768 bytes per sector)
+ * (512 pages) * (256 bytes per page)
+ */
+
+#define M25P_M25P1_SECTOR_SHIFT 15 /* Sector size 1 << 15 = 65,536 */
+#define M25P_M25P1_NSECTORS 4
+#define M25P_M25P1_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
+#define M25P_M25P1_NPAGES 512
+
+/* M25P32 capacity is 4,194,304 bytes:
+ * (64 sectors) * (65,536 bytes per sector)
+ * (16384 pages) * (256 bytes per page)
+ */
+
+#define M25P_M25P32_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */
+#define M25P_M25P32_NSECTORS 64
+#define M25P_M25P32_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
+#define M25P_M25P32_NPAGES 16384
+
+/* M25P64 capacity is 8,338,608 bytes:
+ * (128 sectors) * (65,536 bytes per sector)
+ * (32768 pages) * (256 bytes per page)
+ */
+
+#define M25P_M25P64_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */
+#define M25P_M25P64_NSECTORS 128
+#define M25P_M25P64_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
+#define M25P_M25P64_NPAGES 32768
+
+/* M25P128 capacity is 16,777,216 bytes:
+ * (64 sectors) * (262,144 bytes per sector)
+ * (65536 pages) * (256 bytes per page)
+ */
+
+#define M25P_M25P128_SECTOR_SHIFT 18 /* Sector size 1 << 18 = 262,144 */
+#define M25P_M25P128_NSECTORS 64
+#define M25P_M25P128_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
+#define M25P_M25P128_NPAGES 65536
+
+/* Instructions */
+/* Command Value N Description Addr Dummy Data */
+#define M25P_WREN 0x06 /* 1 Write Enable 0 0 0 */
+#define M25P_WRDI 0x04 /* 1 Write Disable 0 0 0 */
+#define M25P_RDID 0x9f /* 1 Read Identification 0 0 1-3 */
+#define M25P_RDSR 0x05 /* 1 Read Status Register 0 0 >=1 */
+#define M25P_WRSR 0x01 /* 1 Write Status Register 0 0 1 */
+#define M25P_READ 0x03 /* 1 Read Data Bytes 3 0 >=1 */
+#define M25P_FAST_READ 0x0b /* 1 Higher speed read 3 1 >=1 */
+#define M25P_PP 0x02 /* 1 Page Program 3 0 1-256 */
+#define M25P_SE 0xd8 /* 1 Sector Erase 3 0 0 */
+#define M25P_BE 0xc7 /* 1 Bulk Erase 0 0 0 */
+#define M25P_DP 0xb9 /* 2 Deep power down 0 0 0 */
+#define M25P_RES 0xab /* 2 Read Electronic Signature 0 3 >=1 */
+
+/* NOTE 1: All parts, NOTE 2: M25P632/M25P64 */
+
+/* Status register bit definitions */
+
+#define M25P_SR_WIP (1 << 0) /* Bit 0: Write in progress bit */
+#define M25P_SR_WEL (1 << 1) /* Bit 1: Write enable latch bit */
+#define M25P_SR_BP_SHIFT (2) /* Bits 2-4: Block protect bits */
+#define M25P_SR_BP_MASK (7 << M25P_SR_BP_SHIFT)
+# define M25P_SR_BP_NONE (0 << M25P_SR_BP_SHIFT) /* Unprotected */
+# define M25P_SR_BP_UPPER64th (1 << M25P_SR_BP_SHIFT) /* Upper 64th */
+# define M25P_SR_BP_UPPER32nd (2 << M25P_SR_BP_SHIFT) /* Upper 32nd */
+# define M25P_SR_BP_UPPER16th (3 << M25P_SR_BP_SHIFT) /* Upper 16th */
+# define M25P_SR_BP_UPPER8th (4 << M25P_SR_BP_SHIFT) /* Upper 8th */
+# define M25P_SR_BP_UPPERQTR (5 << M25P_SR_BP_SHIFT) /* Upper quarter */
+# define M25P_SR_BP_UPPERHALF (6 << M25P_SR_BP_SHIFT) /* Upper half */
+# define M25P_SR_BP_ALL (7 << M25P_SR_BP_SHIFT) /* All sectors */
+ /* Bits 5-6: Unused, read zero */
+#define M25P_SR_SRWD (1 << 7) /* Bit 7: Status register write protect */
+
+#define M25P_DUMMY 0xa5
+
+/************************************************************************************
+ * Private Types
+ ************************************************************************************/
+
+/* This type represents the state of the MTD device. The struct mtd_dev_s
+ * must appear at the beginning of the definition so that you can freely
+ * cast between pointers to struct mtd_dev_s and struct m25p_dev_s.
+ */
+
+struct m25p_dev_s
+{
+ struct mtd_dev_s mtd; /* MTD interface */
+ FAR struct spi_dev_s *dev; /* Saved SPI interface instance */
+ uint8_t sectorshift; /* 16 or 18 */
+ uint8_t pageshift; /* 8 */
+ uint16_t nsectors; /* 128 or 64 */
+ uint32_t npages; /* 32,768 or 65,536 */
+};
+
+/************************************************************************************
+ * Private Function Prototypes
+ ************************************************************************************/
+
+/* Helpers */
+
+static void m25p_lock(FAR struct spi_dev_s *dev);
+static inline void m25p_unlock(FAR struct spi_dev_s *dev);
+static inline int m25p_readid(struct m25p_dev_s *priv);
+static void m25p_waitwritecomplete(struct m25p_dev_s *priv);
+static void m25p_writeenable(struct m25p_dev_s *priv);
+static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t offset);
+static inline int m25p_bulkerase(struct m25p_dev_s *priv);
+static inline void m25p_pagewrite(struct m25p_dev_s *priv, FAR const uint8_t *buffer,
+ off_t offset);
+
+/* MTD driver methods */
+
+static int m25p_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
+static ssize_t m25p_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR uint8_t *buf);
+static ssize_t m25p_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buf);
+static ssize_t m25p_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
+ FAR uint8_t *buffer);
+static int m25p_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
+
+/************************************************************************************
+ * Private Data
+ ************************************************************************************/
+
+/************************************************************************************
+ * Private Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: m25p_lock
+ ************************************************************************************/
+
+static void m25p_lock(FAR struct spi_dev_s *dev)
+{
+ /* On SPI busses where there are multiple devices, it will be necessary to
+ * lock SPI to have exclusive access to the busses for a sequence of
+ * transfers. The bus should be locked before the chip is selected.
+ *
+ * This is a blocking call and will not return until we have exclusiv access to
+ * the SPI buss. We will retain that exclusive access until the bus is unlocked.
+ */
+
+ (void)SPI_LOCK(dev, true);
+
+ /* After locking the SPI bus, the we also need call the setfrequency, setbits, and
+ * setmode methods to make sure that the SPI is properly configured for the device.
+ * If the SPI buss is being shared, then it may have been left in an incompatible
+ * state.
+ */
+
+ SPI_SETMODE(dev, CONFIG_MP25P_SPIMODE);
+ SPI_SETBITS(dev, 8);
+ (void)SPI_SETFREQUENCY(dev, 20000000);
+}
+
+/************************************************************************************
+ * Name: m25p_unlock
+ ************************************************************************************/
+
+static inline void m25p_unlock(FAR struct spi_dev_s *dev)
+{
+ (void)SPI_LOCK(dev, false);
+}
+
+/************************************************************************************
+ * Name: m25p_readid
+ ************************************************************************************/
+
+static inline int m25p_readid(struct m25p_dev_s *priv)
+{
+ uint16_t manufacturer;
+ uint16_t memory;
+ uint16_t capacity;
+
+ fvdbg("priv: %p\n", priv);
+
+ /* Lock the SPI bus, configure the bus, and select this FLASH part. */
+
+ m25p_lock(priv->dev);
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send the "Read ID (RDID)" command and read the first three ID bytes */
+
+ (void)SPI_SEND(priv->dev, M25P_RDID);
+ manufacturer = SPI_SEND(priv->dev, M25P_DUMMY);
+ memory = SPI_SEND(priv->dev, M25P_DUMMY);
+ capacity = SPI_SEND(priv->dev, M25P_DUMMY);
+
+ /* Deselect the FLASH and unlock the bus */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ m25p_unlock(priv->dev);
+
+ fvdbg("manufacturer: %02x memory: %02x capacity: %02x\n",
+ manufacturer, memory, capacity);
+
+ /* Check for a valid manufacturer and memory type */
+
+ if (manufacturer == M25P_MANUFACTURER && memory == M25P_MEMORY_TYPE)
+ {
+ /* Okay.. is it a FLASH capacity that we understand? */
+
+ if (capacity == M25P_M25P1_CAPACITY)
+ {
+ /* Save the FLASH geometry */
+
+ priv->sectorshift = M25P_M25P1_SECTOR_SHIFT;
+ priv->nsectors = M25P_M25P1_NSECTORS;
+ priv->pageshift = M25P_M25P1_PAGE_SHIFT;
+ priv->npages = M25P_M25P1_NPAGES;
+ return OK;
+ }
+ else if (capacity == M25P_M25P32_CAPACITY)
+ {
+ /* Save the FLASH geometry */
+
+ priv->sectorshift = M25P_M25P32_SECTOR_SHIFT;
+ priv->nsectors = M25P_M25P32_NSECTORS;
+ priv->pageshift = M25P_M25P32_PAGE_SHIFT;
+ priv->npages = M25P_M25P32_NPAGES;
+ return OK;
+ }
+ else if (capacity == M25P_M25P64_CAPACITY)
+ {
+ /* Save the FLASH geometry */
+
+ priv->sectorshift = M25P_M25P64_SECTOR_SHIFT;
+ priv->nsectors = M25P_M25P64_NSECTORS;
+ priv->pageshift = M25P_M25P64_PAGE_SHIFT;
+ priv->npages = M25P_M25P64_NPAGES;
+ return OK;
+ }
+ else if (capacity == M25P_M25P128_CAPACITY)
+ {
+ /* Save the FLASH geometry */
+
+ priv->sectorshift = M25P_M25P128_SECTOR_SHIFT;
+ priv->nsectors = M25P_M25P128_NSECTORS;
+ priv->pageshift = M25P_M25P128_PAGE_SHIFT;
+ priv->npages = M25P_M25P128_NPAGES;
+ return OK;
+ }
+ }
+
+ return -ENODEV;
+}
+
+/************************************************************************************
+ * Name: m25p_waitwritecomplete
+ ************************************************************************************/
+
+static void m25p_waitwritecomplete(struct m25p_dev_s *priv)
+{
+ uint8_t status;
+
+ /* Are we the only device on the bus? */
+
+#ifdef CONFIG_SPI_OWNBUS
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Read Status Register (RDSR)" command */
+
+ (void)SPI_SEND(priv->dev, M25P_RDSR);
+
+ /* Loop as long as the memory is busy with a write cycle */
+
+ do
+ {
+ /* Send a dummy byte to generate the clock needed to shift out the status */
+
+ status = SPI_SEND(priv->dev, M25P_DUMMY);
+ }
+ while ((status & M25P_SR_WIP) != 0);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+
+#else
+
+ /* Loop as long as the memory is busy with a write cycle */
+
+ do
+ {
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Read Status Register (RDSR)" command */
+
+ (void)SPI_SEND(priv->dev, M25P_RDSR);
+
+ /* Send a dummy byte to generate the clock needed to shift out the status */
+
+ status = SPI_SEND(priv->dev, M25P_DUMMY);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+
+ /* Given that writing could take up to few tens of milliseconds, and erasing
+ * could take more. The following short delay in the "busy" case will allow
+ * other peripherals to access the SPI bus.
+ */
+
+ if ((status & M25P_SR_WIP) != 0)
+ {
+ m25p_unlock(priv->dev);
+ usleep(1000);
+ m25p_lock(priv->dev);
+ }
+ }
+ while ((status & M25P_SR_WIP) != 0);
+#endif
+
+ fvdbg("Complete\n");
+}
+
+/************************************************************************************
+ * Name: m25p_writeenable
+ ************************************************************************************/
+
+static void m25p_writeenable(struct m25p_dev_s *priv)
+{
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Write Enable (WREN)" command */
+
+ (void)SPI_SEND(priv->dev, M25P_WREN);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ fvdbg("Enabled\n");
+}
+
+/************************************************************************************
+ * Name: m25p_sectorerase
+ ************************************************************************************/
+
+static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t sector)
+{
+ off_t offset = sector << priv->sectorshift;
+
+ fvdbg("sector: %08lx\n", (long)sector);
+
+ /* Wait for any preceding write to complete. We could simplify things by
+ * perform this wait at the end of each write operation (rather than at
+ * the beginning of ALL operations), but have the wait first will slightly
+ * improve performance.
+ */
+
+ m25p_waitwritecomplete(priv);
+
+ /* Send write enable instruction */
+
+ m25p_writeenable(priv);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send the "Sector Erase (SE)" instruction */
+
+ (void)SPI_SEND(priv->dev, M25P_SE);
+
+ /* Send the sector offset high byte first. For all of the supported
+ * parts, the sector number is completely contained in the first byte
+ * and the values used in the following two bytes don't really matter.
+ */
+
+ (void)SPI_SEND(priv->dev, (offset >> 16) & 0xff);
+ (void)SPI_SEND(priv->dev, (offset >> 8) & 0xff);
+ (void)SPI_SEND(priv->dev, offset & 0xff);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ fvdbg("Erased\n");
+}
+
+/************************************************************************************
+ * Name: m25p_bulkerase
+ ************************************************************************************/
+
+static inline int m25p_bulkerase(struct m25p_dev_s *priv)
+{
+ fvdbg("priv: %p\n", priv);
+
+ /* Wait for any preceding write to complete. We could simplify things by
+ * perform this wait at the end of each write operation (rather than at
+ * the beginning of ALL operations), but have the wait first will slightly
+ * improve performance.
+ */
+
+ m25p_waitwritecomplete(priv);
+
+ /* Send write enable instruction */
+
+ m25p_writeenable(priv);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send the "Bulk Erase (BE)" instruction */
+
+ (void)SPI_SEND(priv->dev, M25P_BE);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ fvdbg("Return: OK\n");
+ return OK;
+}
+
+/************************************************************************************
+ * Name: m25p_pagewrite
+ ************************************************************************************/
+
+static inline void m25p_pagewrite(struct m25p_dev_s *priv, FAR const uint8_t *buffer,
+ off_t page)
+{
+ off_t offset = page << priv->pageshift;
+
+ fvdbg("page: %08lx offset: %08lx\n", (long)page, (long)offset);
+
+ /* Wait for any preceding write to complete. We could simplify things by
+ * perform this wait at the end of each write operation (rather than at
+ * the beginning of ALL operations), but have the wait first will slightly
+ * improve performance.
+ */
+
+ m25p_waitwritecomplete(priv);
+
+ /* Enable the write access to the FLASH */
+
+ m25p_writeenable(priv);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Page Program (PP)" command */
+
+ (void)SPI_SEND(priv->dev, M25P_PP);
+
+ /* Send the page offset high byte first. */
+
+ (void)SPI_SEND(priv->dev, (offset >> 16) & 0xff);
+ (void)SPI_SEND(priv->dev, (offset >> 8) & 0xff);
+ (void)SPI_SEND(priv->dev, offset & 0xff);
+
+ /* Then write the specified number of bytes */
+
+ SPI_SNDBLOCK(priv->dev, buffer, 1 << priv->pageshift);
+
+ /* Deselect the FLASH: Chip Select high */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ fvdbg("Written\n");
+}
+
+/************************************************************************************
+ * Name: m25p_erase
+ ************************************************************************************/
+
+static int m25p_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
+{
+ FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
+ size_t blocksleft = nblocks;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* Lock access to the SPI bus until we complete the erase */
+
+ m25p_lock(priv->dev);
+ while (blocksleft-- > 0)
+ {
+ /* Erase each sector */
+
+ m25p_sectorerase(priv, startblock);
+ startblock++;
+ }
+ m25p_unlock(priv->dev);
+ return (int)nblocks;
+}
+
+/************************************************************************************
+ * Name: m25p_bread
+ ************************************************************************************/
+
+static ssize_t m25p_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR uint8_t *buffer)
+{
+ FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
+ ssize_t nbytes;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* On this device, we can handle the block read just like the byte-oriented read */
+
+ nbytes = m25p_read(dev, startblock << priv->pageshift, nblocks << priv->pageshift, buffer);
+ if (nbytes > 0)
+ {
+ return nbytes >> priv->pageshift;
+ }
+ return (int)nbytes;
+}
+
+/************************************************************************************
+ * Name: m25p_bwrite
+ ************************************************************************************/
+
+static ssize_t m25p_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR const uint8_t *buffer)
+{
+ FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
+ size_t blocksleft = nblocks;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* Lock the SPI bus and write each page to FLASH */
+
+ m25p_lock(priv->dev);
+ while (blocksleft-- > 0)
+ {
+ m25p_pagewrite(priv, buffer, startblock);
+ startblock++;
+ }
+ m25p_unlock(priv->dev);
+
+ return nblocks;
+}
+
+/************************************************************************************
+ * Name: m25p_read
+ ************************************************************************************/
+
+static ssize_t m25p_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
+ FAR uint8_t *buffer)
+{
+ FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
+
+ fvdbg("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
+
+ /* Wait for any preceding write to complete. We could simplify things by
+ * perform this wait at the end of each write operation (rather than at
+ * the beginning of ALL operations), but have the wait first will slightly
+ * improve performance.
+ */
+
+ m25p_waitwritecomplete(priv);
+
+ /* Lock the SPI bus and select this FLASH part */
+
+ m25p_lock(priv->dev);
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Read from Memory " instruction */
+
+ (void)SPI_SEND(priv->dev, M25P_READ);
+
+ /* Send the page offset high byte first. */
+
+ (void)SPI_SEND(priv->dev, (offset >> 16) & 0xff);
+ (void)SPI_SEND(priv->dev, (offset >> 8) & 0xff);
+ (void)SPI_SEND(priv->dev, offset & 0xff);
+
+ /* Then read all of the requested bytes */
+
+ SPI_RECVBLOCK(priv->dev, buffer, nbytes);
+
+ /* Deselect the FLASH and unlock the SPI bus */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ m25p_unlock(priv->dev);
+ fvdbg("return nbytes: %d\n", (int)nbytes);
+ return nbytes;
+}
+
+/************************************************************************************
+ * Name: m25p_ioctl
+ ************************************************************************************/
+
+static int m25p_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
+{
+ FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
+ int ret = -EINVAL; /* Assume good command with bad parameters */
+
+ fvdbg("cmd: %d \n", cmd);
+
+ switch (cmd)
+ {
+ case MTDIOC_GEOMETRY:
+ {
+ FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
+ if (geo)
+ {
+ /* Populate the geometry structure with information need to know
+ * the capacity and how to access the device.
+ *
+ * NOTE: that the device is treated as though it where just an array
+ * of fixed size blocks. That is most likely not true, but the client
+ * will expect the device logic to do whatever is necessary to make it
+ * appear so.
+ */
+
+ geo->blocksize = (1 << priv->pageshift);
+ geo->erasesize = (1 << priv->sectorshift);
+ geo->neraseblocks = priv->nsectors;
+ ret = OK;
+
+ fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
+ geo->blocksize, geo->erasesize, geo->neraseblocks);
+ }
+ }
+ break;
+
+ case MTDIOC_BULKERASE:
+ {
+ /* Erase the entire device */
+
+ m25p_lock(priv->dev);
+ ret = m25p_bulkerase(priv);
+ m25p_unlock(priv->dev);
+ }
+ break;
+
+ case MTDIOC_XIPBASE:
+ default:
+ ret = -ENOTTY; /* Bad command */
+ break;
+ }
+
+ fvdbg("return %d\n", ret);
+ return ret;
+}
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: m25p_initialize
+ *
+ * Description:
+ * Create an initialize MTD device instance. MTD devices are not registered
+ * in the file system, but are created as instances that can be bound to
+ * other functions (such as a block or character driver front end).
+ *
+ ************************************************************************************/
+
+FAR struct mtd_dev_s *m25p_initialize(FAR struct spi_dev_s *dev)
+{
+ FAR struct m25p_dev_s *priv;
+ int ret;
+
+ fvdbg("dev: %p\n", dev);
+
+ /* Allocate a state structure (we allocate the structure instead of using
+ * a fixed, static allocation so that we can handle multiple FLASH devices.
+ * The current implementation would handle only one FLASH part per SPI
+ * device (only because of the SPIDEV_FLASH definition) and so would have
+ * to be extended to handle multiple FLASH parts on the same SPI bus.
+ */
+
+ priv = (FAR struct m25p_dev_s *)kmalloc(sizeof(struct m25p_dev_s));
+ if (priv)
+ {
+ /* Initialize the allocated structure */
+
+ priv->mtd.erase = m25p_erase;
+ priv->mtd.bread = m25p_bread;
+ priv->mtd.bwrite = m25p_bwrite;
+ priv->mtd.read = m25p_read;
+ priv->mtd.ioctl = m25p_ioctl;
+ priv->dev = dev;
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(dev, SPIDEV_FLASH, false);
+
+ /* Identify the FLASH chip and get its capacity */
+
+ ret = m25p_readid(priv);
+ if (ret != OK)
+ {
+ /* Unrecognized! Discard all of that work we just did and return NULL */
+
+ fdbg("Unrecognized\n");
+ kfree(priv);
+ priv = NULL;
+ }
+ }
+
+ /* Return the implementation-specific state structure as the MTD device */
+
+ fvdbg("Return %p\n", priv);
+ return (FAR struct mtd_dev_s *)priv;
+}
diff --git a/nuttx/drivers/mtd/rammtd.c b/nuttx/drivers/mtd/rammtd.c
new file mode 100644
index 000000000..82a7191ea
--- /dev/null
+++ b/nuttx/drivers/mtd/rammtd.c
@@ -0,0 +1,417 @@
+/****************************************************************************
+ * drivers/mtd/rammtd.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_RAMMTD_BLOCKSIZE
+# define CONFIG_RAMMTD_BLOCKSIZE 512
+#endif
+
+#ifndef CONFIG_RAMMTD_ERASESIZE
+# define CONFIG_RAMMTD_ERASESIZE 4096
+#endif
+
+#ifndef CONFIG_RAMMTD_ERASESTATE
+# define CONFIG_RAMMTD_ERASESTATE 0xff
+#endif
+
+#if CONFIG_RAMMTD_ERASESTATE != 0xff && CONFIG_RAMMTD_ERASESTATE != 0x00
+# error "Unsupported value for CONFIG_RAMMTD_ERASESTATE"
+#endif
+
+#if CONFIG_RAMMTD_BLOCKSIZE > CONFIG_RAMMTD_ERASESIZE
+# error "Must have CONFIG_RAMMTD_BLOCKSIZE <= CONFIG_RAMMTD_ERASESIZE"
+#endif
+
+#undef CONFIG_RAMMTD_BLKPER
+#define CONFIG_RAMMTD_BLKPER (CONFIG_RAMMTD_ERASESIZE/CONFIG_RAMMTD_BLOCKSIZE)
+
+#if CONFIG_RAMMTD_BLKPER*CONFIG_RAMMTD_BLOCKSIZE != CONFIG_RAMMTD_ERASESIZE
+# error "CONFIG_RAMMTD_ERASESIZE must be an even multiple of CONFIG_RAMMTD_BLOCKSIZE"
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This type represents the state of the MTD device. The struct mtd_dev_s
+ * must appear at the beginning of the definition so that you can freely
+ * cast between pointers to struct mtd_dev_s and struct ram_dev_s.
+ */
+
+struct ram_dev_s
+{
+ struct mtd_dev_s mtd; /* MTD device */
+ FAR uint8_t *start; /* Start of RAM */
+ size_t nblocks; /* Number of erase blocks */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* The RAM MTD driver may be useful just as it is, but another good use of
+ * the RAM MTD driver is as a FLASH simulation -- to support testing of FLASH
+ * based logic without having FLASH. CONFIG_RAMMTD_FLASHSIM will add some
+ * extra logic to improve the level of FLASH simulation.
+ */
+
+#define ram_read(dest, src, len) memcpy(dest, src, len)
+#ifdef CONFIG_RAMMTD_FLASHSIM
+static void *ram_write(FAR void *dest, FAR const void *src, size_t len);
+#else
+# define ram_write(dest, src, len) memcpy(dest, src, len)
+#endif
+
+/* MTD driver methods */
+
+static int ram_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
+static ssize_t ram_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR uint8_t *buf);
+static ssize_t ram_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR const uint8_t *buf);
+static int ram_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ram_write
+ ****************************************************************************/
+
+#ifdef CONFIG_RAMMTD_FLASHSIM
+static void *ram_write(FAR void *dest, FAR const void *src, size_t len)
+{
+ FAR uint8_t *pout = (FAR uint8_t *)dest;
+ FAR const uint8_t *pin = (FAR const uint8_t *)src;
+
+ while (len-- > 0)
+ {
+ /* Get the source and destination values */
+
+ uint8_t oldvalue = *pout;
+ uint8_t srcvalue = *pin++;
+ uint8_t newvalue;
+
+ /* Get the new destination value, accounting for bits that cannot be
+ * changes because they are not in the erased state.
+ */
+
+#if CONFIG_RAMMTD_ERASESTATE == 0xff
+ newvalue = oldvalue & srcvalue; /* We can only clear bits */
+#else /* CONFIG_RAMMTD_ERASESTATE == 0x00 */
+ newvalue = oldvalue | srcvalue; /* We can only set bits */
+#endif
+
+ /* Report any attempt to change the value of bits that are not in the
+ * erased state.
+ */
+
+#ifdef CONFIG_DEBUG
+ if (newvalue != srcvalue)
+ {
+ dbg("ERROR: Bad write: source=%02x dest=%02x result=%02x\n",
+ srcvalue, oldvalue, newvalue);
+ }
+#endif
+
+ /* Write the modified value to simulated FLASH */
+
+ *pout++ = newvalue;
+ }
+
+ return dest;
+}
+#endif
+
+/****************************************************************************
+ * Name: ram_erase
+ ****************************************************************************/
+
+static int ram_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
+{
+ FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev;
+ off_t offset;
+ size_t nbytes;
+
+ DEBUGASSERT(dev);
+
+ /* Don't let the erase exceed the size of the ram buffer */
+
+ if (startblock >= priv->nblocks)
+ {
+ return 0;
+ }
+
+ if (startblock + nblocks > priv->nblocks)
+ {
+ nblocks = priv->nblocks - startblock;
+ }
+
+ /* Convert the erase block to a logical block and the number of blocks
+ * in logical block numbers
+ */
+
+ startblock *= CONFIG_RAMMTD_BLKPER;
+ nblocks *= CONFIG_RAMMTD_BLKPER;
+
+ /* Get the offset corresponding to the first block and the size
+ * corresponding to the number of blocks.
+ */
+
+ offset = startblock * CONFIG_RAMMTD_BLOCKSIZE;
+ nbytes = nblocks * CONFIG_RAMMTD_BLOCKSIZE;
+
+ /* Then erase the data in RAM */
+
+ memset(&priv->start[offset], CONFIG_RAMMTD_ERASESTATE, nbytes);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: ram_bread
+ ****************************************************************************/
+
+static ssize_t ram_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR uint8_t *buf)
+{
+ FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev;
+ off_t offset;
+ off_t maxblock;
+ size_t nbytes;
+
+ DEBUGASSERT(dev && buf);
+
+ /* Don't let the read exceed the size of the ram buffer */
+
+ maxblock = priv->nblocks * CONFIG_RAMMTD_BLKPER;
+ if (startblock >= maxblock)
+ {
+ return 0;
+ }
+
+ if (startblock + nblocks > maxblock)
+ {
+ nblocks = maxblock - startblock;
+ }
+
+ /* Get the offset corresponding to the first block and the size
+ * corresponding to the number of blocks.
+ */
+
+ offset = startblock * CONFIG_RAMMTD_BLOCKSIZE;
+ nbytes = nblocks * CONFIG_RAMMTD_BLOCKSIZE;
+
+ /* Then read the data frp, RAM */
+
+ ram_read(buf, &priv->start[offset], nbytes);
+ return nblocks;
+}
+
+/****************************************************************************
+ * Name: ram_bwrite
+ ****************************************************************************/
+
+static ssize_t ram_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buf)
+{
+ FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev;
+ off_t offset;
+ off_t maxblock;
+ size_t nbytes;
+
+ DEBUGASSERT(dev && buf);
+
+ /* Don't let the write exceed the size of the ram buffer */
+
+ maxblock = priv->nblocks * CONFIG_RAMMTD_BLKPER;
+ if (startblock >= maxblock)
+ {
+ return 0;
+ }
+
+ if (startblock + nblocks > maxblock)
+ {
+ nblocks = maxblock - startblock;
+ }
+
+ /* Get the offset corresponding to the first block and the size
+ * corresponding to the number of blocks.
+ */
+
+ offset = startblock * CONFIG_RAMMTD_BLOCKSIZE;
+ nbytes = nblocks * CONFIG_RAMMTD_BLOCKSIZE;
+
+ /* Then write the data to RAM */
+
+ ram_write(&priv->start[offset], buf, nbytes);
+ return nblocks;
+}
+
+/****************************************************************************
+ * Name: ram_ioctl
+ ****************************************************************************/
+
+static int ram_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
+{
+ FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev;
+ int ret = -EINVAL; /* Assume good command with bad parameters */
+
+ switch (cmd)
+ {
+ case MTDIOC_GEOMETRY:
+ {
+ FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
+ if (geo)
+ {
+ /* Populate the geometry structure with information need to know
+ * the capacity and how to access the device.
+ */
+
+ geo->blocksize = CONFIG_RAMMTD_BLOCKSIZE;
+ geo->erasesize = CONFIG_RAMMTD_ERASESIZE;
+ geo->neraseblocks = priv->nblocks;
+ ret = OK;
+ }
+ }
+ break;
+
+ case MTDIOC_XIPBASE:
+ {
+ FAR void **ppv = (FAR void**)((uintptr_t)arg);
+ if (ppv)
+ {
+ /* Return (void*) base address of device memory */
+
+ *ppv = (FAR void*)priv->start;
+ ret = OK;
+ }
+ }
+ break;
+
+ case MTDIOC_BULKERASE:
+ {
+ size_t size = priv->nblocks * CONFIG_RAMMTD_ERASESIZE;
+
+ /* Erase the entire device */
+
+ memset(priv->start, CONFIG_RAMMTD_ERASESTATE, size);
+ ret = OK;
+ }
+ break;
+
+ default:
+ ret = -ENOTTY; /* Bad command */
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rammtd_initialize
+ *
+ * Description:
+ * Create and initialize a RAM MTD device instance.
+ *
+ * Input Parameters:
+ * start - Address of the beginning of the allocated RAM regions.
+ * size - The size in bytes of the allocated RAM region.
+ *
+ ****************************************************************************/
+
+FAR struct mtd_dev_s *rammtd_initialize(FAR uint8_t *start, size_t size)
+{
+ FAR struct ram_dev_s *priv;
+ size_t nblocks;
+
+ /* Create an instance of the RAM MTD device state structure */
+
+ priv = (FAR struct ram_dev_s *)kmalloc(sizeof(struct ram_dev_s));
+ if (!priv)
+ {
+ fdbg("Failed to allocate the RAM MTD state structure\n");
+ return NULL;
+ }
+
+ /* Force the size to be an even number of the erase block size */
+
+ nblocks = size / CONFIG_RAMMTD_ERASESIZE;
+ if (nblocks <= 0)
+ {
+ fdbg("Need to provide at least one full erase block\n");
+ return NULL;
+ }
+
+ /* Perform initialization as necessary */
+
+ priv->mtd.erase = ram_erase;
+ priv->mtd.bread = ram_bread;
+ priv->mtd.bwrite = ram_bwrite;
+ priv->mtd.ioctl = ram_ioctl;
+ priv->mtd.erase = ram_erase;
+
+ priv->start = start;
+ priv->nblocks = nblocks;
+ return &priv->mtd;
+}
diff --git a/nuttx/drivers/mtd/ramtron.c b/nuttx/drivers/mtd/ramtron.c
new file mode 100644
index 000000000..074545e2d
--- /dev/null
+++ b/nuttx/drivers/mtd/ramtron.c
@@ -0,0 +1,674 @@
+/************************************************************************************
+ * drivers/mtd/ramtron.c
+ * Driver for SPI-based RAMTRON NVRAM Devices FM25V10 and others (not tested)
+ *
+ * Copyright (C) 2011 Uros Platise. All rights reserved.
+ * Copyright (C) 2009-2010, 2012 Gregory Nutt. All rights reserved.
+ * Author: Uros Platise <uros.platise@isotel.eu>
+ * Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ************************************************************************************/
+
+/* OPTIONS:
+ * - additional non-jedec standard device: FM25H20
+ * must be enabled with the CONFIG_RAMTRON_FRAM_NON_JEDEC=y
+ *
+ * NOTE:
+ * - frequency is fixed to desired max by RAMTRON_INIT_CLK_MAX
+ * if new devices with different speed arrive, then SETFREQUENCY()
+ * needs to handle freq changes and INIT_CLK_MAX must be reduced
+ * to fit all devices. Note that STM32_SPI driver is prone to
+ * too high freq. parameters and limit it within physical constraints.
+ *
+ * TODO:
+ * - add support for sleep
+ * - add support for faster read FSTRD command
+ */
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/spi.h>
+#include <nuttx/mtd.h>
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+
+/* RAMTRON devices are flat!
+ * For purpose of the VFAT file system we emulate the following configuration:
+ */
+
+#define RAMTRON_EMULATE_SECTOR_SHIFT 9
+#define RAMTRON_EMULATE_PAGE_SHIFT 9
+
+/* RAMTRON Indentification register values */
+
+#define RAMTRON_MANUFACTURER 0x7F
+#define RAMTRON_MEMORY_TYPE 0xC2
+
+/* Instructions:
+ * Command Value N Description Addr Dummy Data */
+#define RAMTRON_WREN 0x06 /* 1 Write Enable 0 0 0 */
+#define RAMTRON_WRDI 0x04 /* 1 Write Disable 0 0 0 */
+#define RAMTRON_RDSR 0x05 /* 1 Read Status Register 0 0 >=1 */
+#define RAMTRON_WRSR 0x01 /* 1 Write Status Register 0 0 1 */
+#define RAMTRON_READ 0x03 /* 1 Read Data Bytes A 0 >=1 */
+#define RAMTRON_FSTRD 0x0b /* 1 Higher speed read A 1 >=1 */
+#define RAMTRON_WRITE 0x02 /* 1 Write A 0 1-256 */
+#define RAMTRON_SLEEP 0xb9 // TODO:
+#define RAMTRON_RDID 0x9f /* 1 Read Identification 0 0 1-3 */
+#define RAMTRON_SN 0xc3 // TODO:
+
+
+/* Status register bit definitions */
+
+#define RAMTRON_SR_WIP (1 << 0) /* Bit 0: Write in progress bit */
+#define RAMTRON_SR_WEL (1 << 1) /* Bit 1: Write enable latch bit */
+#define RAMTRON_SR_BP_SHIFT (2) /* Bits 2-4: Block protect bits */
+#define RAMTRON_SR_BP_MASK (7 << RAMTRON_SR_BP_SHIFT)
+# define RAMTRON_SR_BP_NONE (0 << RAMTRON_SR_BP_SHIFT) /* Unprotected */
+# define RAMTRON_SR_BP_UPPER64th (1 << RAMTRON_SR_BP_SHIFT) /* Upper 64th */
+# define RAMTRON_SR_BP_UPPER32nd (2 << RAMTRON_SR_BP_SHIFT) /* Upper 32nd */
+# define RAMTRON_SR_BP_UPPER16th (3 << RAMTRON_SR_BP_SHIFT) /* Upper 16th */
+# define RAMTRON_SR_BP_UPPER8th (4 << RAMTRON_SR_BP_SHIFT) /* Upper 8th */
+# define RAMTRON_SR_BP_UPPERQTR (5 << RAMTRON_SR_BP_SHIFT) /* Upper quarter */
+# define RAMTRON_SR_BP_UPPERHALF (6 << RAMTRON_SR_BP_SHIFT) /* Upper half */
+# define RAMTRON_SR_BP_ALL (7 << RAMTRON_SR_BP_SHIFT) /* All sectors */
+#define RAMTRON_SR_SRWD (1 << 7) /* Bit 7: Status register write protect */
+
+#define RAMTRON_DUMMY 0xa5
+
+/************************************************************************************
+ * Private Types
+ ************************************************************************************/
+
+struct ramtron_parts_s
+{
+ const char *name;
+ uint8_t id1;
+ uint8_t id2;
+ uint32_t size;
+ uint8_t addr_len;
+ uint32_t speed;
+};
+
+/* This type represents the state of the MTD device. The struct mtd_dev_s
+ * must appear at the beginning of the definition so that you can freely
+ * cast between pointers to struct mtd_dev_s and struct ramtron_dev_s.
+ */
+
+struct ramtron_dev_s
+{
+ struct mtd_dev_s mtd; /* MTD interface */
+ FAR struct spi_dev_s *dev; /* Saved SPI interface instance */
+ uint8_t sectorshift;
+ uint8_t pageshift;
+ uint16_t nsectors;
+ uint32_t npages;
+ const struct ramtron_parts_s *part; /* part instance */
+};
+
+/************************************************************************************
+ * Supported Part Lists
+ ************************************************************************************/
+
+// Defines the initial speed compatible with all devices. In case of RAMTRON
+// the defined devices within the part list have all the same speed.
+#define RAMTRON_INIT_CLK_MAX 40000000UL
+
+static struct ramtron_parts_s ramtron_parts[] =
+{
+ {
+ "FM25V02", /* name */
+ 0x22, /* id1 */
+ 0x00, /* id2 */
+ 32L*1024L, /* size */
+ 2, /* addr_len */
+ 40000000 /* speed */
+ },
+ {
+ "FM25VN02", /* name */
+ 0x22, /* id1 */
+ 0x01, /* id2 */
+ 32L*1024L, /* size */
+ 2, /* addr_len */
+ 40000000 /* speed */
+ },
+ {
+ "FM25V05", /* name */
+ 0x23, /* id1 */
+ 0x00, /* id2 */
+ 64L*1024L, /* size */
+ 2, /* addr_len */
+ 40000000 /* speed */
+ },
+ {
+ "FM25VN05", /* name */
+ 0x23, /* id1 */
+ 0x01, /* id2 */
+ 64L*1024L, /* size */
+ 2, /* addr_len */
+ 40000000 /* speed */
+ },
+ {
+ "FM25V10", /* name */
+ 0x24, /* id1 */
+ 0x00, /* id2 */
+ 128L*1024L, /* size */
+ 3, /* addr_len */
+ 40000000 /* speed */
+ },
+ {
+ "FM25VN10", /* name */
+ 0x24, /* id1 */
+ 0x01, /* id2 */
+ 128L*1024L, /* size */
+ 3, /* addr_len */
+ 40000000 /* speed */
+ },
+#ifdef CONFIG_RAMTRON_FRAM_NON_JEDEC
+ {
+ "FM25H20", /* name */
+ 0xff, /* id1 */
+ 0xff, /* id2 */
+ 256L*1024L, /* size */
+ 3, /* addr_len */
+ 40000000 /* speed */
+ },
+ {
+ NULL, /* name */
+ 0, /* id1 */
+ 0, /* id2 */
+ 0, /* size */
+ 0, /* addr_len */
+ 0 /* speed */
+ }
+#endif
+};
+
+
+/************************************************************************************
+ * Private Function Prototypes
+ ************************************************************************************/
+
+/* Helpers */
+
+static void ramtron_lock(FAR struct spi_dev_s *dev);
+static inline void ramtron_unlock(FAR struct spi_dev_s *dev);
+static inline int ramtron_readid(struct ramtron_dev_s *priv);
+static void ramtron_waitwritecomplete(struct ramtron_dev_s *priv);
+static void ramtron_writeenable(struct ramtron_dev_s *priv);
+static inline void ramtron_pagewrite(struct ramtron_dev_s *priv, FAR const uint8_t *buffer,
+ off_t offset);
+
+/* MTD driver methods */
+
+static int ramtron_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
+static ssize_t ramtron_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR uint8_t *buf);
+static ssize_t ramtron_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buf);
+static ssize_t ramtron_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
+ FAR uint8_t *buffer);
+static int ramtron_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
+
+/************************************************************************************
+ * Private Data
+ ************************************************************************************/
+
+/************************************************************************************
+ * Private Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: ramtron_lock
+ ************************************************************************************/
+
+static void ramtron_lock(FAR struct spi_dev_s *dev)
+{
+ /* On SPI busses where there are multiple devices, it will be necessary to
+ * lock SPI to have exclusive access to the busses for a sequence of
+ * transfers. The bus should be locked before the chip is selected.
+ *
+ * This is a blocking call and will not return until we have exclusiv access to
+ * the SPI buss. We will retain that exclusive access until the bus is unlocked.
+ */
+
+ (void)SPI_LOCK(dev, true);
+
+ /* After locking the SPI bus, the we also need call the setfrequency, setbits, and
+ * setmode methods to make sure that the SPI is properly configured for the device.
+ * If the SPI buss is being shared, then it may have been left in an incompatible
+ * state.
+ */
+
+ SPI_SETMODE(dev, SPIDEV_MODE3);
+ SPI_SETBITS(dev, 8);
+
+ (void)SPI_SETFREQUENCY(dev, RAMTRON_INIT_CLK_MAX);
+}
+
+/************************************************************************************
+ * Name: ramtron_unlock
+ ************************************************************************************/
+
+static inline void ramtron_unlock(FAR struct spi_dev_s *dev)
+{
+ (void)SPI_LOCK(dev, false);
+}
+
+/************************************************************************************
+ * Name: ramtron_readid
+ ************************************************************************************/
+
+static inline int ramtron_readid(struct ramtron_dev_s *priv)
+{
+ uint16_t manufacturer, memory, capacity, part;
+ int i;
+
+ fvdbg("priv: %p\n", priv);
+
+ /* Lock the SPI bus, configure the bus, and select this FLASH part. */
+
+ ramtron_lock(priv->dev);
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send the "Read ID (RDID)" command and read the first three ID bytes */
+
+ (void)SPI_SEND(priv->dev, RAMTRON_RDID);
+ for (i=0; i<6; i++) manufacturer = SPI_SEND(priv->dev, RAMTRON_DUMMY);
+ memory = SPI_SEND(priv->dev, RAMTRON_DUMMY);
+ capacity = SPI_SEND(priv->dev, RAMTRON_DUMMY); // fram.id1
+ part = SPI_SEND(priv->dev, RAMTRON_DUMMY); // fram.id2
+
+ /* Deselect the FLASH and unlock the bus */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ ramtron_unlock(priv->dev);
+
+ // Select part from the part list
+ for (priv->part = ramtron_parts;
+ priv->part->name != NULL && !(priv->part->id1 == capacity && priv->part->id2 == part);
+ priv->part++);
+
+ if (priv->part->name) {
+ fvdbg("RAMTRON %s of size %d bytes (mf:%02x mem:%02x cap:%02x part:%02x)\n",
+ priv->part->name, priv->part->size, manufacturer, memory, capacity, part);
+
+ priv->sectorshift = RAMTRON_EMULATE_SECTOR_SHIFT;
+ priv->nsectors = priv->part->size / (1 << RAMTRON_EMULATE_SECTOR_SHIFT);
+ priv->pageshift = RAMTRON_EMULATE_PAGE_SHIFT;
+ priv->npages = priv->part->size / (1 << RAMTRON_EMULATE_PAGE_SHIFT);
+ return OK;
+ }
+
+ fvdbg("RAMTRON device not found\n");
+ return -ENODEV;
+}
+
+/************************************************************************************
+ * Name: ramtron_waitwritecomplete
+ ************************************************************************************/
+
+static void ramtron_waitwritecomplete(struct ramtron_dev_s *priv)
+{
+ uint8_t status;
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Read Status Register (RDSR)" command */
+
+ (void)SPI_SEND(priv->dev, RAMTRON_RDSR);
+
+ /* Loop as long as the memory is busy with a write cycle */
+
+ do
+ {
+ /* Send a dummy byte to generate the clock needed to shift out the status */
+
+ status = SPI_SEND(priv->dev, RAMTRON_DUMMY);
+ }
+ while ((status & RAMTRON_SR_WIP) != 0);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ fvdbg("Complete\n");
+}
+
+/************************************************************************************
+ * Name: ramtron_writeenable
+ ************************************************************************************/
+
+static void ramtron_writeenable(struct ramtron_dev_s *priv)
+{
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Write Enable (WREN)" command */
+
+ (void)SPI_SEND(priv->dev, RAMTRON_WREN);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ fvdbg("Enabled\n");
+}
+
+/************************************************************************************
+ * Name: ramtron_sendaddr
+ ************************************************************************************/
+
+static inline void ramtron_sendaddr(const struct ramtron_dev_s *priv, uint32_t addr)
+{
+ DEBUGASSERT(priv->part->addr_len == 3 || priv->part->addr_len == 2);
+
+ if (priv->part->addr_len == 3)
+ (void)SPI_SEND(priv->dev, (addr >> 16) & 0xff);
+
+ (void)SPI_SEND(priv->dev, (addr >> 8) & 0xff);
+ (void)SPI_SEND(priv->dev, addr & 0xff);
+}
+
+/************************************************************************************
+ * Name: ramtron_pagewrite
+ ************************************************************************************/
+
+static inline void ramtron_pagewrite(struct ramtron_dev_s *priv, FAR const uint8_t *buffer,
+ off_t page)
+{
+ off_t offset = page << priv->pageshift;
+
+ fvdbg("page: %08lx offset: %08lx\n", (long)page, (long)offset);
+
+ /* Wait for any preceding write to complete. We could simplify things by
+ * perform this wait at the end of each write operation (rather than at
+ * the beginning of ALL operations), but have the wait first will slightly
+ * improve performance.
+ */
+
+ ramtron_waitwritecomplete(priv);
+
+ /* Enable the write access to the FLASH */
+
+ ramtron_writeenable(priv);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Page Program (PP)" command */
+
+ (void)SPI_SEND(priv->dev, RAMTRON_WRITE);
+
+ /* Send the page offset high byte first. */
+
+ ramtron_sendaddr(priv, offset);
+
+ /* Then write the specified number of bytes */
+
+ SPI_SNDBLOCK(priv->dev, buffer, 1 << priv->pageshift);
+
+ /* Deselect the FLASH: Chip Select high */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ fvdbg("Written\n");
+}
+
+/************************************************************************************
+ * Name: ramtron_erase
+ ************************************************************************************/
+
+static int ramtron_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
+{
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+ fvdbg("On RAMTRON devices erasing makes no sense, returning as OK\n");
+ return (int)nblocks;
+}
+
+/************************************************************************************
+ * Name: ramtron_bread
+ ************************************************************************************/
+
+static ssize_t ramtron_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR uint8_t *buffer)
+{
+ FAR struct ramtron_dev_s *priv = (FAR struct ramtron_dev_s *)dev;
+ ssize_t nbytes;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* On this device, we can handle the block read just like the byte-oriented read */
+
+ nbytes = ramtron_read(dev, startblock << priv->pageshift, nblocks << priv->pageshift, buffer);
+ if (nbytes > 0)
+ {
+ return nbytes >> priv->pageshift;
+ }
+ return (int)nbytes;
+}
+
+/************************************************************************************
+ * Name: ramtron_bwrite
+ ************************************************************************************/
+
+static ssize_t ramtron_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR const uint8_t *buffer)
+{
+ FAR struct ramtron_dev_s *priv = (FAR struct ramtron_dev_s *)dev;
+ size_t blocksleft = nblocks;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* Lock the SPI bus and write each page to FLASH */
+
+ ramtron_lock(priv->dev);
+ while (blocksleft-- > 0)
+ {
+ ramtron_pagewrite(priv, buffer, startblock);
+ startblock++;
+ }
+ ramtron_unlock(priv->dev);
+
+ return nblocks;
+}
+
+/************************************************************************************
+ * Name: ramtron_read
+ ************************************************************************************/
+
+static ssize_t ramtron_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
+ FAR uint8_t *buffer)
+{
+ FAR struct ramtron_dev_s *priv = (FAR struct ramtron_dev_s *)dev;
+
+ fvdbg("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
+
+ /* Wait for any preceding write to complete. We could simplify things by
+ * perform this wait at the end of each write operation (rather than at
+ * the beginning of ALL operations), but have the wait first will slightly
+ * improve performance.
+ */
+
+ ramtron_waitwritecomplete(priv);
+
+ /* Lock the SPI bus and select this FLASH part */
+
+ ramtron_lock(priv->dev);
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Read from Memory " instruction */
+
+ (void)SPI_SEND(priv->dev, RAMTRON_READ);
+
+ /* Send the page offset high byte first. */
+
+ ramtron_sendaddr(priv, offset);
+
+ /* Then read all of the requested bytes */
+
+ SPI_RECVBLOCK(priv->dev, buffer, nbytes);
+
+ /* Deselect the FLASH and unlock the SPI bus */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ ramtron_unlock(priv->dev);
+ fvdbg("return nbytes: %d\n", (int)nbytes);
+ return nbytes;
+}
+
+/************************************************************************************
+ * Name: ramtron_ioctl
+ ************************************************************************************/
+
+static int ramtron_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
+{
+ FAR struct ramtron_dev_s *priv = (FAR struct ramtron_dev_s *)dev;
+ int ret = -EINVAL; /* Assume good command with bad parameters */
+
+ fvdbg("cmd: %d \n", cmd);
+
+ switch (cmd)
+ {
+ case MTDIOC_GEOMETRY:
+ {
+ FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
+ if (geo)
+ {
+ /* Populate the geometry structure with information need to know
+ * the capacity and how to access the device.
+ *
+ * NOTE: that the device is treated as though it where just an array
+ * of fixed size blocks. That is most likely not true, but the client
+ * will expect the device logic to do whatever is necessary to make it
+ * appear so.
+ */
+
+ geo->blocksize = (1 << priv->pageshift);
+ geo->erasesize = (1 << priv->sectorshift);
+ geo->neraseblocks = priv->nsectors;
+ ret = OK;
+
+ fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
+ geo->blocksize, geo->erasesize, geo->neraseblocks);
+ }
+ }
+ break;
+
+ case MTDIOC_BULKERASE:
+ fvdbg("BULDERASE: Makes no sense in ramtron. Let's confirm operation as OK\n");
+ ret = OK;
+ break;
+
+ case MTDIOC_XIPBASE:
+ default:
+ ret = -ENOTTY; /* Bad command */
+ break;
+ }
+
+ fvdbg("return %d\n", ret);
+ return ret;
+}
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: ramtron_initialize
+ *
+ * Description:
+ * Create an initialize MTD device instance. MTD devices are not registered
+ * in the file system, but are created as instances that can be bound to
+ * other functions (such as a block or character driver front end).
+ *
+ ************************************************************************************/
+
+FAR struct mtd_dev_s *ramtron_initialize(FAR struct spi_dev_s *dev)
+{
+ FAR struct ramtron_dev_s *priv;
+
+ fvdbg("dev: %p\n", dev);
+
+ /* Allocate a state structure (we allocate the structure instead of using
+ * a fixed, static allocation so that we can handle multiple FLASH devices.
+ * The current implementation would handle only one FLASH part per SPI
+ * device (only because of the SPIDEV_FLASH definition) and so would have
+ * to be extended to handle multiple FLASH parts on the same SPI bus.
+ */
+
+ priv = (FAR struct ramtron_dev_s *)kmalloc(sizeof(struct ramtron_dev_s));
+ if (priv)
+ {
+ /* Initialize the allocated structure */
+
+ priv->mtd.erase = ramtron_erase;
+ priv->mtd.bread = ramtron_bread;
+ priv->mtd.bwrite = ramtron_bwrite;
+ priv->mtd.read = ramtron_read;
+ priv->mtd.ioctl = ramtron_ioctl;
+ priv->dev = dev;
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(dev, SPIDEV_FLASH, false);
+
+ /* Identify the FLASH chip and get its capacity */
+
+ if (ramtron_readid(priv) != OK)
+ {
+ /* Unrecognized! Discard all of that work we just did and return NULL */
+ kfree(priv);
+ priv = NULL;
+ }
+ }
+
+ /* Return the implementation-specific state structure as the MTD device */
+
+ fvdbg("Return %p\n", priv);
+ return (FAR struct mtd_dev_s *)priv;
+}
diff --git a/nuttx/drivers/mtd/skeleton.c b/nuttx/drivers/mtd/skeleton.c
new file mode 100644
index 000000000..a2fb98238
--- /dev/null
+++ b/nuttx/drivers/mtd/skeleton.c
@@ -0,0 +1,275 @@
+/****************************************************************************
+ * drivers/mtd/skeleton.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This type represents the state of the MTD device. The struct mtd_dev_s
+ * must appear at the beginning of the definition so that you can freely
+ * cast between pointers to struct mtd_dev_s and struct skel_dev_s.
+ */
+
+struct skel_dev_s
+{
+ struct mtd_dev_s mtd;
+
+ /* Other implementation specific data may follow here */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD driver methods */
+
+static int skel_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
+static ssize_t skel_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR uint8_t *buf);
+static ssize_t skel_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR const uint8_t *buf);
+static ssize_t skel_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
+ FAR uint8_t *buffer);
+static int skel_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct skel_dev_s g_skeldev =
+{
+ { skel_erase, skel_rbead, skel_bwrite, skel_read, skel_ioctl },
+ /* Initialization of any other implemenation specific data goes here */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: skel_erase
+ ****************************************************************************/
+
+static int skel_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
+{
+ FAR struct skel_dev_s *priv = (FAR struct skel_dev_s *)dev;
+
+ /* The interface definition assumes that all erase blocks ar the same size.
+ * If that is not true for this particular device, then transform the
+ * start block and nblocks as necessary.
+ */
+
+ /* Erase the specified blocks and return status (OK or a negated errno) */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: skel_bread
+ ****************************************************************************/
+
+static ssize_t skel_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR uint8_t *buf)
+{
+ FAR struct skel_dev_s *priv = (FAR struct skel_dev_s *)dev;
+
+ /* The interface definition assumes that all read/write blocks ar the same size.
+ * If that is not true for this particular device, then transform the
+ * start block and nblocks as necessary.
+ */
+
+ /* Read the specified blocks into the provided user buffer and return status
+ * (The positive, number of blocks actually read or a negated errno).
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: skel_bwrite
+ ****************************************************************************/
+
+static ssize_t skel_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR const uint8_t *buf)
+{
+ FAR struct skel_dev_s *priv = (FAR struct skel_dev_s *)dev;
+
+ /* The interface definition assumes that all read/write blocks ar the same size.
+ * If that is not true for this particular device, then transform the
+ * start block and nblocks as necessary.
+ */
+
+ /* Write the specified blocks from the provided user buffer and return status
+ * (The positive, number of blocks actually written or a negated errno)
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: skel_read
+ ****************************************************************************/
+
+static ssize_t skel_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
+ FAR uint8_t *buffer)
+{
+ FAR struct skel_dev_s *priv = (FAR struct skel_dev_s *)dev;
+
+ /* Some devices may support byte oriented read (optional). Byte-oriented
+ * writing is inherently block oriented on most MTD devices and is not supported.
+ * It is recommended that low-level drivers not support read() if it requires
+ * buffering -- let the higher level logic handle that. If the read method is
+ * not implemented, just set the method pointer to NULL in the struct mtd_dev_s
+ * instance.
+ */
+
+ /* The interface definition assumes that all read/write blocks ar the same size.
+ * If that is not true for this particular device, then transform the
+ * start block and nblocks as necessary.
+ */
+
+ /* Read the specified blocks into the provided user buffer and return status
+ * (The positive, number of blocks actually read or a negated errno)
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: skel_ioctl
+ ****************************************************************************/
+
+static int skel_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
+{
+ FAR struct skel_dev_s *priv = (FAR struct skel_dev_s *)dev;
+ int ret = -EINVAL; /* Assume good command with bad parameters */
+
+ switch (cmd)
+ {
+ case MTDIOC_GEOMETRY:
+ {
+ FAR struct mtd_geometry_s *geo = (FARstruct mtd_geometry_s *)arg;
+ if (geo)
+ {
+ /* Populate the geometry structure with information need to know
+ * the capacity and how to access the device.
+ *
+ * NOTE: that the device is treated as though it where just an array
+ * of fixed size blocks. That is most likely not true, but the client
+ * will expect the device logic to do whatever is necessary to make it
+ * appear so.
+ */
+
+ geo->blocksize = 512; /* Size of one read/write block */
+ geo->erasesize = 4096; /* Size of one erase block */
+ geo->neraseblocks = 1024; /* Number of erase blocks */
+ ret = OK;
+ }
+ }
+ break;
+
+ case MTDIOC_XIPBASE:
+ {
+ FAR void **ppv = (FAR void**)arg;
+
+ if (ppv)
+ {
+ /* If media is directly acccesible, return (void*) base address
+ * of device memory. NULL otherwise. It is acceptable to omit
+ * this case altogether and simply return -ENOTTY.
+ */
+
+ *ppv = NULL;
+ ret = OK;
+ }
+ }
+ break;
+
+ case MTDIOC_BULKERASE:
+ {
+ /* Erase the entire device */
+
+ ret = OK;
+ }
+ break;
+
+ default:
+ ret = -ENOTTY; /* Bad command */
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: skel_initialize
+ *
+ * Description:
+ * Create and initialize an MTD device instance. MTD devices are not
+ * registered in the file system, but are created as instances that can
+ * be bound to other functions (such as a block or character driver front
+ * end).
+ *
+ ****************************************************************************/
+
+void skel_initialize(void)
+{
+ /* Perform initialization as necessary */
+
+ /* Return the implementation-specific state structure as the MTD device */
+
+ return (FAR struct mtd_dev_s *)&g_skeldev;
+}
diff --git a/nuttx/drivers/mtd/sst25.c b/nuttx/drivers/mtd/sst25.c
new file mode 100644
index 000000000..01838f078
--- /dev/null
+++ b/nuttx/drivers/mtd/sst25.c
@@ -0,0 +1,1250 @@
+/************************************************************************************
+ * drivers/mtd/m25px.c
+ * Driver for SPI-based SST25 FLASH.
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ************************************************************************************/
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/spi.h>
+#include <nuttx/mtd.h>
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+/* Configuration ********************************************************************/
+/* Per the data sheet, the SST25 parts can be driven with either SPI mode 0 (CPOL=0
+ * and CPHA=0) or mode 3 (CPOL=1 and CPHA=1). But I have heard that other devices
+ * can operate in mode 0 or 1. So you may need to specify CONFIG_SST25_SPIMODE to
+ * select the best mode for your device. If CONFIG_SST25_SPIMODE is not defined,
+ * mode 0 will be used.
+ */
+
+#ifndef CONFIG_SST25_SPIMODE
+# define CONFIG_SST25_SPIMODE SPIDEV_MODE0
+#endif
+
+/* SPI Frequency. May be up to 25MHz. */
+
+#ifndef CONFIG_SST25_SPIFREQUENCY
+# define CONFIG_SST25_SPIFREQUENCY 20000000
+#endif
+
+/* There is a bug in the current code when using the higher speed AAI write sequence.
+ * The nature of the bug is that the WRDI instruction is not working. At the end
+ * of the AAI sequence, the status register continues to report that the SST25 is
+ * write enabled (WEL bit) and in AAI mode (AAI bit). This *must* be fixed in any
+ * production code if you want to have proper write performance.
+ */
+
+#warning "REVISIT"
+#undef CONFIG_SST25_SLOWWRITE
+#define CONFIG_SST25_SLOWWRITE 1
+
+/* SST25 Instructions ***************************************************************/
+/* Command Value Description Addr Data */
+/* Dummy */
+#define SST25_READ 0x03 /* Read data bytes 3 0 >=1 */
+#define SST25_FAST_READ 0x0b /* Higher speed read 3 1 >=1 */
+#define SST25_SE 0x20 /* 4Kb Sector erase 3 0 0 */
+#define SST25_BE32 0x52 /* 32Kbit block Erase 3 0 0 */
+#define SST25_BE64 0xd8 /* 64Kbit block Erase 3 0 0 */
+#define SST25_CE 0xc7 /* Chip erase 0 0 0 */
+#define SST25_CE_ALT 0x60 /* Chip erase (alternate) 0 0 0 */
+#define SST25_BP 0x02 /* Byte program 3 0 1 */
+#define SST25_AAI 0xad /* Auto address increment 3 0 >=2 */
+#define SST25_RDSR 0x05 /* Read status register 0 0 >=1 */
+#define SST25_EWSR 0x50 /* Write enable status 0 0 0 */
+#define SST25_WRSR 0x01 /* Write Status Register 0 0 1 */
+#define SST25_WREN 0x06 /* Write Enable 0 0 0 */
+#define SST25_WRDI 0x04 /* Write Disable 0 0 0 */
+#define SST25_RDID 0xab /* Read Identification 0 0 >=1 */
+#define SST25_RDID_ALT 0x90 /* Read Identification (alt) 0 0 >=1 */
+#define SST25_JEDEC_ID 0x9f /* JEDEC ID read 0 0 >=3 */
+#define SST25_EBSY 0x70 /* Enable SO RY/BY# status 0 0 0 */
+#define SST25_DBSY 0x80 /* Disable SO RY/BY# status 0 0 0 */
+
+/* SST25 Registers ******************************************************************/
+/* Read ID (RDID) register values */
+
+#define SST25_MANUFACTURER 0xbf /* SST manufacturer ID */
+#define SST25_VF032_DEVID 0x20 /* SSTVF032B device ID */
+
+/* JEDEC Read ID register values */
+
+#define SST25_JEDEC_MANUFACTURER 0xbf /* SST manufacturer ID */
+#define SST25_JEDEC_MEMORY_TYPE 0x25 /* SST25 memory type */
+#define SST25_JEDEC_MEMORY_CAPACITY 0x4a /* SST25VF032B memory capacity */
+
+/* Status register bit definitions */
+
+#define SST25_SR_BUSY (1 << 0) /* Bit 0: Write in progress */
+#define SST25_SR_WEL (1 << 1) /* Bit 1: Write enable latch bit */
+#define SST25_SR_BP_SHIFT (2) /* Bits 2-5: Block protect bits */
+#define SST25_SR_BP_MASK (15 << SST25_SR_BP_SHIFT)
+# define SST25_SR_BP_NONE (0 << SST25_SR_BP_SHIFT) /* Unprotected */
+# define SST25_SR_BP_UPPER64th (1 << SST25_SR_BP_SHIFT) /* Upper 64th */
+# define SST25_SR_BP_UPPER32nd (2 << SST25_SR_BP_SHIFT) /* Upper 32nd */
+# define SST25_SR_BP_UPPER16th (3 << SST25_SR_BP_SHIFT) /* Upper 16th */
+# define SST25_SR_BP_UPPER8th (4 << SST25_SR_BP_SHIFT) /* Upper 8th */
+# define SST25_SR_BP_UPPERQTR (5 << SST25_SR_BP_SHIFT) /* Upper quarter */
+# define SST25_SR_BP_UPPERHALF (6 << SST25_SR_BP_SHIFT) /* Upper half */
+# define SST25_SR_BP_ALL (7 << SST25_SR_BP_SHIFT) /* All sectors */
+#define SST25_SR_AAI (1 << 6) /* Bit 6: Auto Address increment programming */
+#define SST25_SR_SRWD (1 << 7) /* Bit 7: Status register write protect */
+
+#define SST25_DUMMY 0xa5
+
+/* Chip Geometries ******************************************************************/
+/* SST25VF512 capacity is 512Kbit (64Kbit x 8) = 64Kb (8Kb x 8)*/
+/* SST25VF010 capacity is 1Mbit (128Kbit x 8) = 128Kb (16Kb x 8*/
+/* SST25VF520 capacity is 2Mbit (256Kbit x 8) = 256Kb (32Kb x 8) */
+/* SST25VF540 capacity is 4Mbit (512Kbit x 8) = 512Kb (64Kb x 8) */
+/* SST25VF080 capacity is 8Mbit (1024Kbit x 8) = 1Mb (128Kb x 8) */
+/* SST25VF016 capacity is 16Mbit (2048Kbit x 8) = 2Mb (256Kb x 8) */
+/* Not yet supported */
+
+/* SST25VF032 capacity is 32Mbit (4096Kbit x 8) = 4Mb (512kb x 8) */
+
+#define SST25_VF032_SECTOR_SHIFT 12 /* Sector size 1 << 12 = 4Kb */
+#define SST25_VF032_NSECTORS 1024 /* 1024 sectors x 4096 bytes/sector = 4Mb */
+
+#ifdef CONFIG_SST25_SECTOR512 /* Simulate a 512 byte sector */
+# define SST25_SECTOR_SHIFT 9 /* Sector size 1 << 9 = 512 bytes */
+# define SST25_SECTOR_SIZE 512 /* Sector size = 512 bytes */
+#endif
+
+#define SST25_ERASED_STATE 0xff /* State of FLASH when erased */
+
+/* Cache flags */
+
+#define SST25_CACHE_VALID (1 << 0) /* 1=Cache has valid data */
+#define SST25_CACHE_DIRTY (1 << 1) /* 1=Cache is dirty */
+#define SST25_CACHE_ERASED (1 << 2) /* 1=Backing FLASH is erased */
+
+#define IS_VALID(p) ((((p)->flags) & SST25_CACHE_VALID) != 0)
+#define IS_DIRTY(p) ((((p)->flags) & SST25_CACHE_DIRTY) != 0)
+#define IS_ERASED(p) ((((p)->flags) & SST25_CACHE_DIRTY) != 0)
+
+#define SET_VALID(p) do { (p)->flags |= SST25_CACHE_VALID; } while (0)
+#define SET_DIRTY(p) do { (p)->flags |= SST25_CACHE_DIRTY; } while (0)
+#define SET_ERASED(p) do { (p)->flags |= SST25_CACHE_DIRTY; } while (0)
+
+#define CLR_VALID(p) do { (p)->flags &= ~SST25_CACHE_VALID; } while (0)
+#define CLR_DIRTY(p) do { (p)->flags &= ~SST25_CACHE_DIRTY; } while (0)
+#define CLR_ERASED(p) do { (p)->flags &= ~SST25_CACHE_DIRTY; } while (0)
+
+/************************************************************************************
+ * Private Types
+ ************************************************************************************/
+
+/* This type represents the state of the MTD device. The struct mtd_dev_s must
+ * appear at the beginning of the definition so that you can freely cast between
+ * pointers to struct mtd_dev_s and struct sst25_dev_s.
+ */
+
+struct sst25_dev_s
+{
+ struct mtd_dev_s mtd; /* MTD interface */
+ FAR struct spi_dev_s *dev; /* Saved SPI interface instance */
+ uint16_t nsectors; /* Number of erase sectors */
+ uint8_t sectorshift; /* Log2 of erase sector size */
+
+#if defined(CONFIG_SST25_SECTOR512) && !defined(CONFIG_SST25_READONLY)
+ uint8_t flags; /* Buffered sector flags */
+ uint16_t esectno; /* Erase sector number in the cache*/
+ FAR uint8_t *sector; /* Allocated sector data */
+#endif
+};
+
+/************************************************************************************
+ * Private Function Prototypes
+ ************************************************************************************/
+
+/* Helpers */
+
+static void sst25_lock(FAR struct spi_dev_s *dev);
+static inline void sst25_unlock(FAR struct spi_dev_s *dev);
+static inline int sst25_readid(FAR struct sst25_dev_s *priv);
+#ifndef CONFIG_SST25_READONLY
+static void sst25_unprotect(FAR struct spi_dev_s *dev);
+#endif
+static uint8_t sst25_waitwritecomplete(FAR struct sst25_dev_s *priv);
+static inline void sst25_wren(FAR struct sst25_dev_s *priv);
+static inline void sst25_wrdi(FAR struct sst25_dev_s *priv);
+static void sst25_sectorerase(FAR struct sst25_dev_s *priv, off_t offset);
+static inline int sst25_chiperase(FAR struct sst25_dev_s *priv);
+static void sst25_byteread(FAR struct sst25_dev_s *priv, FAR uint8_t *buffer,
+ off_t address, size_t nbytes);
+#ifndef CONFIG_SST25_READONLY
+#ifdef CONFIG_SST25_SLOWWRITE
+static void sst25_bytewrite(FAR struct sst25_dev_s *priv, FAR const uint8_t *buffer,
+ off_t address, size_t nbytes);
+#else
+static void sst25_wordwrite(FAR struct sst25_dev_s *priv, FAR const uint8_t *buffer,
+ off_t address, size_t nbytes);
+#endif
+#ifdef CONFIG_SST25_SECTOR512
+static void sst25_cacheflush(struct sst25_dev_s *priv);
+static FAR uint8_t *sst25_cacheread(struct sst25_dev_s *priv, off_t sector);
+static void sst25_cacheerase(struct sst25_dev_s *priv, off_t sector);
+static void sst25_cachewrite(FAR struct sst25_dev_s *priv, FAR const uint8_t *buffer,
+ off_t sector, size_t nsectors);
+#endif
+#endif
+
+/* MTD driver methods */
+
+static int sst25_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks);
+static ssize_t sst25_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR uint8_t *buf);
+static ssize_t sst25_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buf);
+static ssize_t sst25_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
+ FAR uint8_t *buffer);
+static int sst25_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
+
+/************************************************************************************
+ * Private Data
+ ************************************************************************************/
+
+/************************************************************************************
+ * Private Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: sst25_lock
+ ************************************************************************************/
+
+static void sst25_lock(FAR struct spi_dev_s *dev)
+{
+ /* On SPI busses where there are multiple devices, it will be necessary to
+ * lock SPI to have exclusive access to the busses for a sequence of
+ * transfers. The bus should be locked before the chip is selected.
+ *
+ * This is a blocking call and will not return until we have exclusiv access to
+ * the SPI buss. We will retain that exclusive access until the bus is unlocked.
+ */
+
+ (void)SPI_LOCK(dev, true);
+
+ /* After locking the SPI bus, the we also need call the setfrequency, setbits, and
+ * setmode methods to make sure that the SPI is properly configured for the device.
+ * If the SPI buss is being shared, then it may have been left in an incompatible
+ * state.
+ */
+
+ SPI_SETMODE(dev, CONFIG_SST25_SPIMODE);
+ SPI_SETBITS(dev, 8);
+ (void)SPI_SETFREQUENCY(dev, CONFIG_SST25_SPIFREQUENCY);
+}
+
+/************************************************************************************
+ * Name: sst25_unlock
+ ************************************************************************************/
+
+static inline void sst25_unlock(FAR struct spi_dev_s *dev)
+{
+ (void)SPI_LOCK(dev, false);
+}
+
+/************************************************************************************
+ * Name: sst25_readid
+ ************************************************************************************/
+
+static inline int sst25_readid(struct sst25_dev_s *priv)
+{
+ uint16_t manufacturer;
+ uint16_t memory;
+ uint16_t capacity;
+
+ fvdbg("priv: %p\n", priv);
+
+ /* Lock the SPI bus, configure the bus, and select this FLASH part. */
+
+ sst25_lock(priv->dev);
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send the "Read ID (RDID)" command and read the first three ID bytes */
+
+ (void)SPI_SEND(priv->dev, SST25_JEDEC_ID);
+ manufacturer = SPI_SEND(priv->dev, SST25_DUMMY);
+ memory = SPI_SEND(priv->dev, SST25_DUMMY);
+ capacity = SPI_SEND(priv->dev, SST25_DUMMY);
+
+ /* Deselect the FLASH and unlock the bus */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ sst25_unlock(priv->dev);
+
+ fvdbg("manufacturer: %02x memory: %02x capacity: %02x\n",
+ manufacturer, memory, capacity);
+
+ /* Check for a valid manufacturer and memory type */
+
+ if (manufacturer == SST25_JEDEC_MANUFACTURER && memory == SST25_JEDEC_MEMORY_TYPE)
+ {
+ /* Okay.. is it a FLASH capacity that we understand? This should be extended
+ * support other members of the SST25 family.
+ */
+
+ if (capacity == SST25_JEDEC_MEMORY_CAPACITY)
+ {
+ /* Save the FLASH geometry */
+
+ priv->sectorshift = SST25_VF032_SECTOR_SHIFT;
+ priv->nsectors = SST25_VF032_NSECTORS;
+ return OK;
+ }
+ }
+
+ return -ENODEV;
+}
+
+/************************************************************************************
+ * Name: sst25_unprotect
+ ************************************************************************************/
+
+#ifndef CONFIG_SST25_READONLY
+static void sst25_unprotect(FAR struct spi_dev_s *dev)
+{
+ /* Select this FLASH part */
+
+ SPI_SELECT(dev, SPIDEV_FLASH, true);
+
+ /* Send "Write enable status (EWSR)" */
+
+ SPI_SEND(dev, SST25_EWSR);
+
+ /* Re-select this FLASH part (This might not be necessary... but is it shown in
+ * the timing diagrams)
+ */
+
+ SPI_SELECT(dev, SPIDEV_FLASH, false);
+ SPI_SELECT(dev, SPIDEV_FLASH, true);
+
+ /* Send "Write enable status (EWSR)" */
+
+ SPI_SEND(dev, SST25_WRSR);
+
+ /* Following by the new status value */
+
+ SPI_SEND(dev, 0);
+}
+#endif
+
+/************************************************************************************
+ * Name: sst25_waitwritecomplete
+ ************************************************************************************/
+
+static uint8_t sst25_waitwritecomplete(struct sst25_dev_s *priv)
+{
+ uint8_t status;
+
+ /* Are we the only device on the bus? */
+
+#ifdef CONFIG_SPI_OWNBUS
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Read Status Register (RDSR)" command */
+
+ (void)SPI_SEND(priv->dev, SST25_RDSR);
+
+ /* Loop as long as the memory is busy with a write cycle */
+
+ do
+ {
+ /* Send a dummy byte to generate the clock needed to shift out the status */
+
+ status = SPI_SEND(priv->dev, SST25_DUMMY);
+ }
+ while ((status & SST25_SR_BUSY) != 0);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+
+#else
+
+ /* Loop as long as the memory is busy with a write cycle */
+
+ do
+ {
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Read Status Register (RDSR)" command */
+
+ (void)SPI_SEND(priv->dev, SST25_RDSR);
+
+ /* Send a dummy byte to generate the clock needed to shift out the status */
+
+ status = SPI_SEND(priv->dev, SST25_DUMMY);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+
+ /* Given that writing could take up to few tens of milliseconds, and erasing
+ * could take more. The following short delay in the "busy" case will allow
+ * other peripherals to access the SPI bus.
+ */
+
+#if 0 /* Makes writes too slow */
+ if ((status & SST25_SR_BUSY) != 0)
+ {
+ sst25_unlock(priv->dev);
+ usleep(1000);
+ sst25_lock(priv->dev);
+ }
+#endif
+ }
+ while ((status & SST25_SR_BUSY) != 0);
+#endif
+
+ return status;
+}
+
+/************************************************************************************
+ * Name: sst25_wren
+ ************************************************************************************/
+
+static inline void sst25_wren(struct sst25_dev_s *priv)
+{
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Write Enable (WREN)" command */
+
+ (void)SPI_SEND(priv->dev, SST25_WREN);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+}
+
+/************************************************************************************
+ * Name: sst25_wrdi
+ ************************************************************************************/
+
+static inline void sst25_wrdi(struct sst25_dev_s *priv)
+{
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Write Disable (WRDI)" command */
+
+ (void)SPI_SEND(priv->dev, SST25_WRDI);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+}
+
+/************************************************************************************
+ * Name: sst25_sectorerase
+ ************************************************************************************/
+
+static void sst25_sectorerase(struct sst25_dev_s *priv, off_t sector)
+{
+ off_t address = sector << priv->sectorshift;
+
+ fvdbg("sector: %08lx\n", (long)sector);
+
+ /* Wait for any preceding write or erase operation to complete. */
+
+ (void)sst25_waitwritecomplete(priv);
+
+ /* Send write enable instruction */
+
+ sst25_wren(priv);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send the "Sector Erase (SE)" instruction */
+
+ (void)SPI_SEND(priv->dev, SST25_SE);
+
+ /* Send the sector address high byte first. Only the most significant bits (those
+ * corresponding to the sector) have any meaning.
+ */
+
+ (void)SPI_SEND(priv->dev, (address >> 16) & 0xff);
+ (void)SPI_SEND(priv->dev, (address >> 8) & 0xff);
+ (void)SPI_SEND(priv->dev, address & 0xff);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+}
+
+/************************************************************************************
+ * Name: sst25_chiperase
+ ************************************************************************************/
+
+static inline int sst25_chiperase(struct sst25_dev_s *priv)
+{
+ fvdbg("priv: %p\n", priv);
+
+ /* Wait for any preceding write or erase operation to complete. */
+
+ (void)sst25_waitwritecomplete(priv);
+
+ /* Send write enable instruction */
+
+ sst25_wren(priv);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send the "Chip Erase (CE)" instruction */
+
+ (void)SPI_SEND(priv->dev, SST25_CE);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ fvdbg("Return: OK\n");
+ return OK;
+}
+
+/************************************************************************************
+ * Name: sst25_byteread
+ ************************************************************************************/
+
+static void sst25_byteread(FAR struct sst25_dev_s *priv, FAR uint8_t *buffer,
+ off_t address, size_t nbytes)
+{
+ uint8_t status;
+
+ fvdbg("address: %08lx nbytes: %d\n", (long)address, (int)nbytes);
+
+ /* Wait for any preceding write or erase operation to complete. */
+
+ status = sst25_waitwritecomplete(priv);
+ DEBUGASSERT((status & (SST25_SR_WEL|SST25_SR_BP_MASK|SST25_SR_AAI)) == 0);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Read from Memory " instruction */
+
+#ifdef CONFIG_SST25_SLOWREAD
+ (void)SPI_SEND(priv->dev, SST25_READ);
+#else
+ (void)SPI_SEND(priv->dev, SST25_FAST_READ);
+#endif
+
+ /* Send the address high byte first. */
+
+ (void)SPI_SEND(priv->dev, (address >> 16) & 0xff);
+ (void)SPI_SEND(priv->dev, (address >> 8) & 0xff);
+ (void)SPI_SEND(priv->dev, address & 0xff);
+
+ /* Send a dummy byte */
+
+#ifndef CONFIG_SST25_SLOWREAD
+ (void)SPI_SEND(priv->dev, SST25_DUMMY);
+#endif
+
+ /* Then read all of the requested bytes */
+
+ SPI_RECVBLOCK(priv->dev, buffer, nbytes);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+}
+
+/************************************************************************************
+ * Name: sst25_bytewrite
+ ************************************************************************************/
+
+#if defined(CONFIG_SST25_SLOWWRITE) && !defined(CONFIG_SST25_READONLY)
+static void sst25_bytewrite(struct sst25_dev_s *priv, FAR const uint8_t *buffer,
+ off_t address, size_t nbytes)
+{
+ uint8_t status;
+
+ fvdbg("address: %08lx nwords: %d\n", (long)address, (int)nbytes);
+ DEBUGASSERT(priv && buffer);
+
+ /* Write each byte individually */
+
+ for (; nbytes > 0; nbytes--)
+ {
+ /* Skip over bytes that are begin written to the erased state */
+
+ if (*buffer != SST25_ERASED_STATE)
+ {
+ /* Wait for any preceding write or erase operation to complete. */
+
+ status = sst25_waitwritecomplete(priv);
+ DEBUGASSERT((status & (SST25_SR_WEL|SST25_SR_BP_MASK|SST25_SR_AAI)) == 0);
+
+ /* Enable write access to the FLASH */
+
+ sst25_wren(priv);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Byte Program (BP)" command */
+
+ (void)SPI_SEND(priv->dev, SST25_BP);
+
+ /* Send the byte address high byte first. */
+
+ (void)SPI_SEND(priv->dev, (address >> 16) & 0xff);
+ (void)SPI_SEND(priv->dev, (address >> 8) & 0xff);
+ (void)SPI_SEND(priv->dev, address & 0xff);
+
+ /* Then write the single byte */
+
+ (void)SPI_SEND(priv->dev, *buffer);
+
+ /* Deselect the FLASH and setup for the next pass through the loop */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ }
+
+ /* Advance to the next byte */
+
+ buffer++;
+ address++;
+ }
+}
+#endif
+
+/************************************************************************************
+ * Name: sst25_wordwrite
+ ************************************************************************************/
+
+#if !defined(CONFIG_SST25_SLOWWRITE) && !defined(CONFIG_SST25_READONLY)
+static void sst25_wordwrite(struct sst25_dev_s *priv, FAR const uint8_t *buffer,
+ off_t address, size_t nbytes)
+{
+ size_t nwords = (nbytes + 1) >> 1;
+ uint8_t status;
+
+ fvdbg("address: %08lx nwords: %d\n", (long)address, (int)nwords);
+ DEBUGASSERT(priv && buffer);
+
+ /* Loop until all of the bytes have been written */
+
+ while (nwords > 0)
+ {
+ /* Skip over any data that is being written to the erased state */
+
+ while (nwords > 0 &&
+ buffer[0] == SST25_ERASED_STATE &&
+ buffer[1] == SST25_ERASED_STATE)
+ {
+ /* Decrement the word count and advance the write position */
+
+ nwords--;
+ buffer += 2;
+ address += 2;
+ }
+
+ /* If there are no further non-erased bytes in the user buffer, then
+ * we are finished.
+ */
+
+ if (nwords <= 0)
+ {
+ return;
+ }
+
+ /* Wait for any preceding write or erase operation to complete. */
+
+ status = sst25_waitwritecomplete(priv);
+ DEBUGASSERT((status & (SST25_SR_WEL|SST25_SR_BP_MASK|SST25_SR_AAI)) == 0);
+
+ /* Enable write access to the FLASH */
+
+ sst25_wren(priv);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Auto Address Increment (AAI)" command */
+
+ (void)SPI_SEND(priv->dev, SST25_AAI);
+
+ /* Send the word address high byte first. */
+
+ (void)SPI_SEND(priv->dev, (address >> 16) & 0xff);
+ (void)SPI_SEND(priv->dev, (address >> 8) & 0xff);
+ (void)SPI_SEND(priv->dev, address & 0xff);
+
+ /* Then write one 16-bit word */
+
+ SPI_SNDBLOCK(priv->dev, buffer, 2);
+
+ /* Deselect the FLASH: Chip Select high */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+
+ /* Decrement the word count and advance the write position */
+
+ nwords--;
+ buffer += 2;
+ address += 2;
+
+ /* Now loop, writing 16-bits of data on each pass through the loop
+ * until all of the words have been transferred or until we encounter
+ * data to be written to the erased state.
+ */
+
+ while (nwords > 0 &&
+ (buffer[0] != SST25_ERASED_STATE ||
+ buffer[1] != SST25_ERASED_STATE))
+ {
+ /* Wait for the preceding write to complete. */
+
+ status = sst25_waitwritecomplete(priv);
+ DEBUGASSERT((status & (SST25_SR_WEL|SST25_SR_BP_MASK|SST25_SR_AAI)) == (SST25_SR_WEL|SST25_SR_AAI));
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Auto Address Increment (AAI)" command with no address */
+
+ (void)SPI_SEND(priv->dev, SST25_AAI);
+
+ /* Then write one 16-bit word */
+
+ SPI_SNDBLOCK(priv->dev, buffer, 2);
+
+ /* Deselect the FLASH: Chip Select high */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+
+ /* Decrement the word count and advance the write position */
+
+ nwords--;
+ buffer += 2;
+ address += 2;
+ }
+
+ /* Disable writing */
+
+ sst25_wrdi(priv);
+ }
+}
+#endif
+
+/************************************************************************************
+ * Name: sst25_cacheflush
+ ************************************************************************************/
+
+#if defined(CONFIG_SST25_SECTOR512) && !defined(CONFIG_SST25_READONLY)
+static void sst25_cacheflush(struct sst25_dev_s *priv)
+{
+ /* If the cached is dirty (meaning that it no longer matches the old FLASH contents)
+ * or was erased (with the cache containing the correct FLASH contents), then write
+ * the cached erase block to FLASH.
+ */
+
+ if (IS_DIRTY(priv) || IS_ERASED(priv))
+ {
+ /* Write entire erase block to FLASH */
+
+#ifdef CONFIG_SST25_SLOWWRITE
+ sst25_bytewrite(priv, priv->sector, (off_t)priv->esectno << priv->sectorshift,
+ (1 << priv->sectorshift));
+#else
+ sst25_wordwrite(priv, priv->sector, (off_t)priv->esectno << priv->sectorshift,
+ (1 << priv->sectorshift));
+#endif
+
+ /* The case is no long dirty and the FLASH is no longer erased */
+
+ CLR_DIRTY(priv);
+ CLR_ERASED(priv);
+ }
+}
+#endif
+
+/************************************************************************************
+ * Name: sst25_cacheread
+ ************************************************************************************/
+
+#if defined(CONFIG_SST25_SECTOR512) && !defined(CONFIG_SST25_READONLY)
+static FAR uint8_t *sst25_cacheread(struct sst25_dev_s *priv, off_t sector)
+{
+ off_t esectno;
+ int shift;
+ int index;
+
+ /* Convert from the 512 byte sector to the erase sector size of the device. For
+ * exmample, if the actual erase sector size if 4Kb (1 << 12), then we first
+ * shift to the right by 3 to get the sector number in 4096 increments.
+ */
+
+ shift = priv->sectorshift - SST25_SECTOR_SHIFT;
+ esectno = sector >> shift;
+ fvdbg("sector: %ld esectno: %d shift=%d\n", sector, esectno, shift);
+
+ /* Check if the requested erase block is already in the cache */
+
+ if (!IS_VALID(priv) || esectno != priv->esectno)
+ {
+ /* No.. Flush any dirty erase block currently in the cache */
+
+ sst25_cacheflush(priv);
+
+ /* Read the erase block into the cache */
+
+ sst25_byteread(priv, priv->sector, (esectno << priv->sectorshift), 1 << priv->sectorshift);
+
+ /* Mark the sector as cached */
+
+ priv->esectno = esectno;
+
+ SET_VALID(priv); /* The data in the cache is valid */
+ CLR_DIRTY(priv); /* It should match the FLASH contents */
+ CLR_ERASED(priv); /* The underlying FLASH has not been erased */
+ }
+
+ /* Get the index to the 512 sector in the erase block that holds the argument */
+
+ index = sector & ((1 << shift) - 1);
+
+ /* Return the address in the cache that holds this sector */
+
+ return &priv->sector[index << SST25_SECTOR_SHIFT];
+}
+#endif
+
+/************************************************************************************
+ * Name: sst25_cacheerase
+ ************************************************************************************/
+
+#if defined(CONFIG_SST25_SECTOR512) && !defined(CONFIG_SST25_READONLY)
+static void sst25_cacheerase(struct sst25_dev_s *priv, off_t sector)
+{
+ FAR uint8_t *dest;
+
+ /* First, make sure that the erase block containing the 512 byte sector is in
+ * the cache.
+ */
+
+ dest = sst25_cacheread(priv, sector);
+
+ /* Erase the block containing this sector if it is not already erased.
+ * The erased indicated will be cleared when the data from the erase sector
+ * is read into the cache and set here when we erase the block.
+ */
+
+ if (!IS_ERASED(priv))
+ {
+ off_t esectno = sector >> (priv->sectorshift - SST25_SECTOR_SHIFT);
+ fvdbg("sector: %ld esectno: %d\n", sector, esectno);
+
+ sst25_sectorerase(priv, esectno);
+ SET_ERASED(priv);
+ }
+
+ /* Put the cached sector data into the erase state and mart the cache as dirty
+ * (but don't update the FLASH yet. The caller will do that at a more optimal
+ * time).
+ */
+
+ memset(dest, SST25_ERASED_STATE, SST25_SECTOR_SIZE);
+ SET_DIRTY(priv);
+}
+#endif
+
+/************************************************************************************
+ * Name: sst25_cachewrite
+ ************************************************************************************/
+
+#if defined(CONFIG_SST25_SECTOR512) && !defined(CONFIG_SST25_READONLY)
+static void sst25_cachewrite(FAR struct sst25_dev_s *priv, FAR const uint8_t *buffer,
+ off_t sector, size_t nsectors)
+{
+ FAR uint8_t *dest;
+
+ for (; nsectors > 0; nsectors--)
+ {
+ /* First, make sure that the erase block containing 512 byte sector is in
+ * memory.
+ */
+
+ dest = sst25_cacheread(priv, sector);
+
+ /* Erase the block containing this sector if it is not already erased.
+ * The erased indicated will be cleared when the data from the erase sector
+ * is read into the cache and set here when we erase the sector.
+ */
+
+ if (!IS_ERASED(priv))
+ {
+ off_t esectno = sector >> (priv->sectorshift - SST25_SECTOR_SHIFT);
+ fvdbg("sector: %ld esectno: %d\n", sector, esectno);
+
+ sst25_sectorerase(priv, esectno);
+ SET_ERASED(priv);
+ }
+
+ /* Copy the new sector data into cached erase block */
+
+ memcpy(dest, buffer, SST25_SECTOR_SIZE);
+ SET_DIRTY(priv);
+
+ /* Set up for the next 512 byte sector */
+
+ buffer += SST25_SECTOR_SIZE;
+ sector++;
+ }
+
+ /* Flush the last erase block left in the cache */
+
+ sst25_cacheflush(priv);
+}
+#endif
+
+/************************************************************************************
+ * Name: sst25_erase
+ ************************************************************************************/
+
+static int sst25_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
+{
+#ifdef CONFIG_SST25_READONLY
+ return -EACESS
+#else
+ FAR struct sst25_dev_s *priv = (FAR struct sst25_dev_s *)dev;
+ size_t blocksleft = nblocks;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* Lock access to the SPI bus until we complete the erase */
+
+ sst25_lock(priv->dev);
+
+ while (blocksleft-- > 0)
+ {
+ /* Erase each sector */
+
+#ifdef CONFIG_SST25_SECTOR512
+ sst25_cacheerase(priv, startblock);
+#else
+ sst25_sectorerase(priv, startblock);
+#endif
+ startblock++;
+ }
+
+#ifdef CONFIG_SST25_SECTOR512
+ /* Flush the last erase block left in the cache */
+
+ sst25_cacheflush(priv);
+#endif
+
+ sst25_unlock(priv->dev);
+ return (int)nblocks;
+#endif
+}
+
+/************************************************************************************
+ * Name: sst25_bread
+ ************************************************************************************/
+
+static ssize_t sst25_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR uint8_t *buffer)
+{
+#ifdef CONFIG_SST25_SECTOR512
+ ssize_t nbytes;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* On this device, we can handle the block read just like the byte-oriented read */
+
+ nbytes = sst25_read(dev, startblock << SST25_SECTOR_SHIFT, nblocks << SST25_SECTOR_SHIFT, buffer);
+ if (nbytes > 0)
+ {
+ return nbytes >> SST25_SECTOR_SHIFT;
+ }
+
+ return (int)nbytes;
+#else
+ FAR struct sst25_dev_s *priv = (FAR struct sst25_dev_s *)dev;
+ ssize_t nbytes;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* On this device, we can handle the block read just like the byte-oriented read */
+
+ nbytes = sst25_read(dev, startblock << priv->sectorshift, nblocks << priv->sectorshift, buffer);
+ if (nbytes > 0)
+ {
+ return nbytes >> priv->sectorshift;
+ }
+
+ return (int)nbytes;
+#endif
+}
+
+/************************************************************************************
+ * Name: sst25_bwrite
+ ************************************************************************************/
+
+static ssize_t sst25_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
+ FAR const uint8_t *buffer)
+{
+#ifdef CONFIG_SST25_READONLY
+ return -EACCESS;
+#else
+ FAR struct sst25_dev_s *priv = (FAR struct sst25_dev_s *)dev;
+
+ fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+ /* Lock the SPI bus and write all of the pages to FLASH */
+
+ sst25_lock(priv->dev);
+
+#if defined(CONFIG_SST25_SECTOR512)
+ sst25_cachewrite(priv, buffer, startblock, nblocks);
+#elif defined(CONFIG_SST25_SLOWWRITE)
+ sst25_bytewrite(priv, buffer, startblock << priv->sectorshift,
+ nblocks << priv->sectorshift);
+#else
+ sst25_wordwrite(priv, buffer, startblock << priv->sectorshift,
+ nblocks << priv->sectorshift);
+#endif
+ sst25_unlock(priv->dev);
+
+ return nblocks;
+#endif
+}
+
+/************************************************************************************
+ * Name: sst25_read
+ ************************************************************************************/
+
+static ssize_t sst25_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
+ FAR uint8_t *buffer)
+{
+ FAR struct sst25_dev_s *priv = (FAR struct sst25_dev_s *)dev;
+
+ fvdbg("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
+
+ /* Lock the SPI bus and select this FLASH part */
+
+ sst25_lock(priv->dev);
+ sst25_byteread(priv, buffer, offset, nbytes);
+ sst25_unlock(priv->dev);
+
+ fvdbg("return nbytes: %d\n", (int)nbytes);
+ return nbytes;
+}
+
+/************************************************************************************
+ * Name: sst25_ioctl
+ ************************************************************************************/
+
+static int sst25_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
+{
+ FAR struct sst25_dev_s *priv = (FAR struct sst25_dev_s *)dev;
+ int ret = -EINVAL; /* Assume good command with bad parameters */
+
+ fvdbg("cmd: %d \n", cmd);
+
+ switch (cmd)
+ {
+ case MTDIOC_GEOMETRY:
+ {
+ FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg);
+ if (geo)
+ {
+ /* Populate the geometry structure with information need to know
+ * the capacity and how to access the device.
+ *
+ * NOTE: that the device is treated as though it where just an array
+ * of fixed size blocks. That is most likely not true, but the client
+ * will expect the device logic to do whatever is necessary to make it
+ * appear so.
+ */
+
+#ifdef CONFIG_SST25_SECTOR512
+ geo->blocksize = (1 << SST25_SECTOR_SHIFT);
+ geo->erasesize = (1 << SST25_SECTOR_SHIFT);
+ geo->neraseblocks = priv->nsectors << (priv->sectorshift - );
+#else
+ geo->blocksize = (1 << priv->sectorshift);
+ geo->erasesize = (1 << priv->sectorshift);
+ geo->neraseblocks = priv->nsectors;
+#endif
+ ret = OK;
+
+ fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
+ geo->blocksize, geo->erasesize, geo->neraseblocks);
+ }
+ }
+ break;
+
+ case MTDIOC_BULKERASE:
+ {
+ /* Erase the entire device */
+
+ sst25_lock(priv->dev);
+ ret = sst25_chiperase(priv);
+ sst25_unlock(priv->dev);
+ }
+ break;
+
+ case MTDIOC_XIPBASE:
+ default:
+ ret = -ENOTTY; /* Bad command */
+ break;
+ }
+
+ fvdbg("return %d\n", ret);
+ return ret;
+}
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: sst25_initialize
+ *
+ * Description:
+ * Create an initialize MTD device instance. MTD devices are not registered
+ * in the file system, but are created as instances that can be bound to
+ * other functions (such as a block or character driver front end).
+ *
+ ************************************************************************************/
+
+FAR struct mtd_dev_s *sst25_initialize(FAR struct spi_dev_s *dev)
+{
+ FAR struct sst25_dev_s *priv;
+ int ret;
+
+ fvdbg("dev: %p\n", dev);
+
+ /* Allocate a state structure (we allocate the structure instead of using
+ * a fixed, static allocation so that we can handle multiple FLASH devices.
+ * The current implementation would handle only one FLASH part per SPI
+ * device (only because of the SPIDEV_FLASH definition) and so would have
+ * to be extended to handle multiple FLASH parts on the same SPI bus.
+ */
+
+ priv = (FAR struct sst25_dev_s *)kzalloc(sizeof(struct sst25_dev_s));
+ if (priv)
+ {
+ /* Initialize the allocated structure */
+
+ priv->mtd.erase = sst25_erase;
+ priv->mtd.bread = sst25_bread;
+ priv->mtd.bwrite = sst25_bwrite;
+ priv->mtd.read = sst25_read;
+ priv->mtd.ioctl = sst25_ioctl;
+ priv->dev = dev;
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(dev, SPIDEV_FLASH, false);
+
+ /* Identify the FLASH chip and get its capacity */
+
+ ret = sst25_readid(priv);
+ if (ret != OK)
+ {
+ /* Unrecognized! Discard all of that work we just did and return NULL */
+
+ fdbg("Unrecognized\n");
+ kfree(priv);
+ priv = NULL;
+ }
+ else
+ {
+ /* Make sure the the FLASH is unprotected so that we can write into it */
+
+#ifndef CONFIG_SST25_READONLY
+ sst25_unprotect(priv->dev);
+#endif
+
+#ifdef CONFIG_SST25_SECTOR512 /* Simulate a 512 byte sector */
+ /* Allocate a buffer for the erase block cache */
+
+ priv->sector = (FAR uint8_t *)kmalloc(1 << priv->sectorshift);
+ if (!priv->sector)
+ {
+ /* Allocation failed! Discard all of that work we just did and return NULL */
+
+ fdbg("Allocation failed\n");
+ kfree(priv);
+ priv = NULL;
+ }
+#endif
+ }
+ }
+
+ /* Return the implementation-specific state structure as the MTD device */
+
+ fvdbg("Return %p\n", priv);
+ return (FAR struct mtd_dev_s *)priv;
+}
diff --git a/nuttx/drivers/net/Kconfig b/nuttx/drivers/net/Kconfig
new file mode 100644
index 000000000..b5c09bf01
--- /dev/null
+++ b/nuttx/drivers/net/Kconfig
@@ -0,0 +1,79 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+config NET_DM90x0
+ bool "Davicom dm9000/dm9010 support"
+ default n
+ ---help---
+ References: Davicom data sheets (DM9000-DS-F03-041906.pdf,
+ DM9010-DS-F01-103006.pdf) and looking at lots of other DM90x0
+ drivers.
+
+config NET_CS89x0
+ bool "CS89x0 support"
+ default n
+ depends on EXPERIMENTAL
+ ---help---
+ Under construction -- do not use
+
+config ENC28J60
+ bool "Microchip ENC28J60 support"
+ default n
+ select SPI
+ ---help---
+ References:
+ ENC28J60 Data Sheet, Stand-Alone Ethernet Controller with SPI Interface,
+ DS39662C, 2008 Microchip Technology Inc.
+if ENC28J60
+config ENC28J60_NINTERFACES
+ int "Number of physical ENC28J60"
+ default 1
+ range 1,1
+ ---help---
+ Specifies the number of physical ENC28J60
+ devices that will be supported.
+
+config ENC28J60_SPIMODE
+ int "SPI mode"
+ default 0
+ ---help---
+ Controls the SPI mode. The ENC28J60 spec says that it supports SPI
+ mode 0,0 only: "The implementation used on this device supports SPI
+ mode 0,0 only. In addition, the SPI port requires that SCK be at Idle
+ in a low state; selectable clock polarity is not supported."
+ However, sometimes you need to tinker with these things.
+
+config ENC28J60_FREQUENCY
+ int "SPI frequency"
+ default 20000000
+ ---help---
+ Define to use a different bus frequency
+
+config ENC28J60_STATS
+ bool "Network statistics support"
+ default n
+ ---help---
+ Collect network statistics
+
+config ENC28J60_HALFDUPPLEX
+ bool "Enable half dupplex"
+ default n
+ ---help---
+ Default is full duplex
+endif
+
+config NET_E1000
+ bool "E1000 support"
+ default n
+
+config NET_SLIP
+ bool "SLIP (serial line) support"
+ default n
+ ---help---
+ Reference: RFC 1055
+
+config NET_VNET
+ bool "VNET support"
+ default n
+
diff --git a/nuttx/drivers/net/Make.defs b/nuttx/drivers/net/Make.defs
new file mode 100644
index 000000000..bb0d6a718
--- /dev/null
+++ b/nuttx/drivers/net/Make.defs
@@ -0,0 +1,71 @@
+############################################################################
+# drivers/net/Make.defs
+#
+# Copyright (C) 2007, 2010-2012 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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 nothing if networking is disabled
+
+ifeq ($(CONFIG_NET),y)
+
+# Include network interface drivers
+
+ifeq ($(CONFIG_NET_DM90x0),y)
+ CSRCS += dm90x0.c
+endif
+
+ifeq ($(CONFIG_NET_CS89x0),y)
+ CSRCS += cs89x0.c
+endif
+
+ifeq ($(CONFIG_ENC28J60),y)
+ CSRCS += enc28j60.c
+endif
+
+ifeq ($(CONFIG_NET_VNET),y)
+ CSRCS += vnet.c
+endif
+
+ifeq ($(CONFIG_NET_E1000),y)
+ CSRCS += e1000.c
+endif
+
+ifeq ($(CONFIG_NET_SLIP),y)
+ CSRCS += slip.c
+endif
+
+# Include network build support
+
+DEPPATH += --dep-path net
+VPATH += :net
+endif
+
diff --git a/nuttx/drivers/net/cs89x0.c b/nuttx/drivers/net/cs89x0.c
new file mode 100644
index 000000000..22b9b87a5
--- /dev/null
+++ b/nuttx/drivers/net/cs89x0.c
@@ -0,0 +1,959 @@
+/****************************************************************************
+ * drivers/net/cs89x0.c
+ *
+ * Copyright (C) 2009-2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#if defined(CONFIG_NET) && defined(CONFIG_NET_CS89x0)
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+#error "Under construction -- do not use"
+
+/* CONFIG_CS89x0_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_CS89x0_NINTERFACES
+# define CONFIG_CS89x0_NINTERFACES 1
+#endif
+
+/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define CS89x0_WDDELAY (1*CLK_TCK)
+#define CS89x0_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define CS89x0_TXTIMEOUT (60*CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)cs89x0->cs_dev.d_buf)
+
+/* If there is only one CS89x0 instance, then mapping the CS89x0 IRQ to
+ * a driver state instance is trivial.
+ */
+
+#if CONFIG_CS89x0_NINTERFACES == 1
+# define cs89x0_mapirq(irq) g_cs89x0[0]
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static FAR struct cs89x0_driver_s *g_cs89x0[CONFIG_CS89x0_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* CS89x0 register access */
+
+static uint16_t cs89x0_getreg(struct cs89x0_driver_s *cs89x0, int offset);
+static void cs89x0_putreg(struct cs89x0_driver_s *cs89x0, int offset,
+ uint16_t value);
+static uint16_t cs89x0_getppreg(struct cs89x0_driver_s *cs89x0, int addr);
+static void cs89x0_putppreg(struct cs89x0_driver_s *cs89x0, int addr,
+ uint16_t value);
+
+/* Common TX logic */
+
+static int cs89x0_transmit(struct cs89x0_driver_s *cs89x0);
+static int cs89x0_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void cs89x0_receive(struct cs89x0_driver_s *cs89x0);
+static void cs89x0_txdone(struct cs89x0_driver_s *cs89x0, uint16_t isq);
+#if CONFIG_CS89x0_NINTERFACES > 1
+static inline FAR struct cs89x0_driver_s *cs89x0_mapirq(int irq);
+#endif
+static int cs89x0_interrupt(int irq, FAR void *context);
+
+/* Watchdog timer expirations */
+
+static void cs89x0_polltimer(int argc, uint32_t arg, ...);
+static void cs89x0_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int cs89x0_ifup(struct uip_driver_s *dev);
+static int cs89x0_ifdown(struct uip_driver_s *dev);
+static int cs89x0_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int cs89x0_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int cs89x0_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: cs89x0_getreg and cs89x0_putreg
+ *
+ * Description:
+ * Read from and write to a CS89x0 register
+ *
+ * Parameters:
+ * cs89x0 - Reference to the driver state structure
+ * offset - Offset to the CS89x0 register
+ * value - Value to be written (cs89x0_putreg only)
+ *
+ * Returned Value:
+ * cs89x0_getreg: The 16-bit value of the register
+ * cs89x0_putreg: None
+ *
+ ****************************************************************************/
+
+static uint16_t cs89x0_getreg(struct cs89x0_driver_s *cs89x0, int offset)
+{
+#ifdef CONFIG_CS89x0_ALIGN16
+ return getreg16(s89x0->cs_base + offset);
+#else
+ return (uint16_t)getreg32(s89x0->cs_base + offset);
+#endif
+}
+
+static void cs89x0_putreg(struct cs89x0_driver_s *cs89x0, int offset, uint16_t value)
+{
+#ifdef CONFIG_CS89x0_ALIGN16
+ return putreg16(value, s89x0->cs_base + offset);
+#else
+ return (uint16_t)putreg32((uint32_t)value, s89x0->cs_base + offset);
+#endif
+}
+
+/****************************************************************************
+ * Function: cs89x0_getppreg and cs89x0_putppreg
+ *
+ * Description:
+ * Read from and write to a CS89x0 page packet register
+ *
+ * Parameters:
+ * cs89x0 - Reference to the driver state structure
+ * addr - Address of the CS89x0 page packet register
+ * value - Value to be written (cs89x0_putppreg only)
+ *
+ * Returned Value:
+ * cs89x0_getppreg: The 16-bit value of the page packet register
+ * cs89x0_putppreg: None
+ *
+ ****************************************************************************/
+
+static uint16_t cs89x0_getppreg(struct cs89x0_driver_s *cs89x0, int addr)
+{
+ /* In memory mode, the CS89x0's internal registers and frame buffers are mapped
+ * into a contiguous 4kb block providing direct access to the internal registers
+ * and frame buffers.
+ */
+
+#ifdef CONFIG_CS89x0_MEMMODE
+ if (cs89x0->cs_memmode)
+ {
+#ifdef CONFIG_CS89x0_ALIGN16
+ return getreg16(s89x0->cs_ppbase + (CS89x0_PDATA_OFFSET << ??));
+#else
+ return (uint16_t)getreg32(s89x0->cs_ppbase + (CS89x0_PDATA_OFFSET << ??));
+#endif
+ }
+
+ /* When configured in I/O mode, the CS89x0 is accessed through eight, 16-bit
+ * I/O ports that in the host system's I/O space.
+ */
+
+ else
+#endif
+ {
+#ifdef CONFIG_CS89x0_ALIGN16
+ putreg16((uint16_t)addr, cs89x0->cs_base + CS89x0_PPTR_OFFSET);
+ return getreg16(s89x0->cs_base + CS89x0_PDATA_OFFSET);
+#else
+ putreg32((uint32_t)addr, cs89x0->cs_base + CS89x0_PPTR_OFFSET);
+ return (uint16_t)getreg32(s89x0->cs_base + CS89x0_PDATA_OFFSET);
+#endif
+ }
+}
+
+static void cs89x0_putppreg(struct cs89x0_driver_s *cs89x0, int addr, uint16_t value)
+{
+ /* In memory mode, the CS89x0's internal registers and frame buffers are mapped
+ * into a contiguous 4kb block providing direct access to the internal registers
+ * and frame buffers.
+ */
+
+#ifdef CONFIG_CS89x0_MEMMODE
+ if (cs89x0->cs_memmode)
+ {
+#ifdef CONFIG_CS89x0_ALIGN16
+ putreg16(value), cs89x0->cs_ppbase + (CS89x0_PDATA_OFFSET << ??));
+#else
+ putreg32((uint32_t)value, cs89x0->cs_ppbase + (CS89x0_PDATA_OFFSET << ??));
+#endif
+ }
+
+ /* When configured in I/O mode, the CS89x0 is accessed through eight, 16-bit
+ * I/O ports that in the host system's I/O space.
+ */
+
+ else
+#endif
+ {
+#ifdef CONFIG_CS89x0_ALIGN16
+ putreg16((uint16_t)addr, cs89x0->cs_base + CS89x0_PPTR_OFFSET);
+ putreg16(value, cs89x0->cs_base + CS89x0_PDATA_OFFSET);
+#else
+ putreg32((uint32_t)addr, cs89x0->cs_base + CS89x0_PPTR_OFFSET);
+ putreg32((uint32_t)value, cs89x0->cs_base + CS89x0_PDATA_OFFSET);
+#endif
+ }
+}
+
+/****************************************************************************
+ * Function: cs89x0_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * cs89x0 - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cs89x0_transmit(struct cs89x0_driver_s *cs89x0)
+{
+ /* Verify that the hardware is ready to send another packet */
+#warning "Missing logic"
+
+ /* Increment statistics */
+#warning "Missing logic"
+
+ /* Disable Ethernet interrupts */
+#warning "Missing logic"
+
+ /* Send the packet: address=cs89x0->cs_dev.d_buf, length=cs89x0->cs_dev.d_len */
+#warning "Missing logic"
+
+ /* Restore Ethernet interrupts */
+#warning "Missing logic"
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ (void)wd_start(cs89x0->cs_txtimeout, CS89x0_TXTIMEOUT, cs89x0_txtimeout, 1, (uint32_t)cs89x0);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: cs89x0_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timesout and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cs89x0_uiptxpoll(struct uip_driver_s *dev)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)dev->d_private;
+
+ /* If the polling resulted in data that should be sent out on the network,
+ * the field d_len is set to a value > 0.
+ */
+
+ if (cs89x0->cs_dev.d_len > 0)
+ {
+ uip_arp_out(&cs89x0->cs_dev);
+ cs89x0_transmit(cs89x0);
+
+ /* Check if there is room in the CS89x0 to hold another packet. If not,
+ * return a non-zero value to terminate the poll.
+ */
+#warning "Missing logic"
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: cs89x0_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * cs89x0 - Reference to the driver state structure
+ * isq - Interrupt status queue value read by interrupt handler
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void cs89x0_receive(struct cs89x0_driver_s *cs89x0, uint16_t isq)
+{
+ uint16_t *dest;
+ uint16_t rxlength;
+ int nbytes;
+
+ /* Check for errors and update statistics */
+
+ rxlength = cs89x0_getreg(PPR_RXLENGTH);
+ if ((isq & RX_OK) == 0)
+ {
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.rx_errors++;
+ if ((isq & RX_RUNT) != 0)
+ {
+ cd89x0->cs_stats.rx_lengtherrors++;
+ }
+ if ((isq & RX_EXTRA_DATA) != 0)
+ {
+ cd89x0->cs_stats.rx_lengtherrors++;
+ }
+ if (isq & RX_CRC_ERROR) != 0)
+ {
+ if (!(isq & (RX_EXTRA_DATA|RX_RUNT)))
+ {
+ cd89x0->cs_stats.rx_crcerrors++;
+ }
+ }
+ if ((isq & RX_DRIBBLE) != 0)
+ {
+ cd89x0->cs_stats.rx_frameerrors++;
+ }
+#endif
+ return;
+ }
+
+ /* Check if the packet is a valid size for the uIP buffer configuration */
+
+ if (rxlength > ???)
+ {
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.rx_errors++;
+ cd89x0->cs_stats.rx_lengtherrors++;
+#endif
+ return;
+ }
+
+ /* Copy the data data from the hardware to cs89x0->cs_dev.d_buf. Set
+ * amount of data in cs89x0->cs_dev.d_len
+ */
+
+ dest = (uint16_t*)cs89x0->cs_dev.d_buf;
+ for (nbytes = 0; nbytes < rxlength; nbytes += sizeof(uint16_t))
+ {
+ *dest++ = cs89x0_getreg(PPR_RXFRAMELOCATION);
+ }
+
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.rx_packets++;
+#endif
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ uip_arp_ipin(&cs89x0->cs_dev);
+ uip_input(&cs89x0->cs_dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (cs89x0->cs_dev.d_len > 0)
+ {
+ uip_arp_out(&cs89x0->cs_dev);
+ cs89x0_transmit(cs89x0);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP))
+ {
+ uip_arp_arpin(&cs89x0->cs_dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (cs89x0->cs_dev.d_len > 0)
+ {
+ cs89x0_transmit(cs89x0);
+ }
+ }
+}
+
+/****************************************************************************
+ * Function: cs89x0_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Parameters:
+ * cs89x0 - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void cs89x0_txdone(struct cs89x0_driver_s *cs89x0, uint16_t isq)
+{
+ /* Check for errors and update statistics. The lower 6-bits of the ISQ
+ * hold the register address causing the interrupt. We got here because
+ * those bits indicated */
+
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.tx_packets++;
+ if ((isq & ISQ_TXEVENT_TXOK) == 0)
+ {
+ cd89x0->cs_stats.tx_errors++;
+ }
+ if ((isq & ISQ_TXEVENT_LOSSOFCRS) != 0)
+ {
+ cd89x0->cs_stats.tx_carriererrors++;
+ }
+ if ((isq & ISQ_TXEVENT_SQEERROR) != 0)
+ {
+ cd89x0->cs_stats.tx_heartbeaterrors++;
+ }
+ if (i(sq & ISQ_TXEVENT_OUTWINDOW) != 0)
+ {
+ cd89x0->cs_stats.tx_windowerrors++;
+ }
+ if (isq & TX_16_COL)
+ {
+ cd89x0->cs_stats.tx_abortederrors++;
+ }
+#endif
+
+ /* If no further xmits are pending, then cancel the TX timeout */
+
+ wd_cancel(cs89x0->cs_txtimeout);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&cs89x0->cs_dev, cs89x0_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: cs89x0_mapirq
+ *
+ * Description:
+ * Map an IRQ number to a CS89x0 device state instance. This is only
+ * necessary to handler the case where the architecture includes more than
+ * on CS89x0 chip.
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ *
+ * Returned Value:
+ * A reference to device state structure (NULL if irq does not correspond
+ * to any CS89x0 device).
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if CONFIG_CS89x0_NINTERFACES > 1
+static inline FAR struct cs89x0_driver_s *cs89x0_mapirq(int irq)
+{
+ int i;
+ for (i = 0; i < CONFIG_CS89x0_NINTERFACES; i++)
+ {
+ if (g_cs89x0[i] && g_cs89x0[i].irq == irq)
+ {
+ return g_cs89x0[i];
+ }
+ }
+ return NULL;
+}
+#endif
+
+/****************************************************************************
+ * Function: cs89x0_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cs89x0_interrupt(int irq, FAR void *context)
+{
+ register struct cs89x0_driver_s *cs89x0 = s89x0_mapirq(irq);
+ uint16_t isq;
+
+#ifdef CONFIG_DEBUG
+ if (!cs89x0)
+ {
+ return -ENODEV;
+ }
+#endif
+
+ /* Read and process all of the events from the ISQ */
+
+ while ((isq = cs89x0_getreg(dev, CS89x0_ISQ_OFFSET)) != 0)
+ {
+ nvdbg("ISQ: %04x\n", isq);
+ switch (isq & ISQ_EVENTMASK)
+ {
+ case ISQ_RXEVENT:
+ cs89x0_receive(cs89x0);
+ break;
+
+ case ISQ_TXEVENT:
+ cs89x0_txdone(cs89x0, isq);
+ break;
+
+ case ISQ_BUFEVENT:
+ if ((isq & ISQ_BUFEVENT_TXUNDERRUN) != 0)
+ {
+ ndbg("Transmit underrun\n");
+#ifdef CONFIG_CS89x0_XMITEARLY
+ cd89x0->cs_txunderrun++;
+ if (cd89x0->cs_txunderrun == 3)
+ {
+ cd89x0->cs_txstart = PPR_TXCMD_TXSTART381;
+ }
+ else if (cd89x0->cs_txunderrun == 6)
+ {
+ cd89x0->cs_txstart = PPR_TXCMD_TXSTARTFULL;
+ }
+#endif
+ }
+ break;
+
+ case ISQ_RXMISSEVENT:
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.rx_missederrors += (isq >>6);
+#endif
+ break;
+
+ case ISQ_TXCOLEVENT:
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.collisions += (isq >>6);
+#endif
+ break;
+ }
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Function: cs89x0_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void cs89x0_txtimeout(int argc, uint32_t arg, ...)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)arg;
+
+ /* Increment statistics and dump debug info */
+#warning "Missing logic"
+
+ /* Then reset the hardware */
+#warning "Missing logic"
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&cs89x0->cs_dev, cs89x0_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: cs89x0_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void cs89x0_polltimer(int argc, uint32_t arg, ...)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)arg;
+
+ /* Check if there is room in the send another TXr packet. */
+#warning "Missing logic"
+
+ /* If so, update TCP timing states and poll uIP for new XMIT data */
+
+ (void)uip_timer(&cs89x0->cs_dev, cs89x0_uiptxpoll, CS89x0_POLLHSEC);
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(cs89x0->cs_txpoll, CS89x0_WDDELAY, cs89x0_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: cs89x0_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cs89x0_ifup(struct uip_driver_s *dev)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)dev->d_private;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initialize the Ethernet interface */
+#warning "Missing logic"
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(cs89x0->cs_txpoll, CS89x0_WDDELAY, cs89x0_polltimer, 1, (uint32_t)cs89x0);
+
+ /* Enable the Ethernet interrupt */
+
+ cs89x0->cs_bifup = true;
+ up_enable_irq(CONFIG_CS89x0_IRQ);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: cs89x0_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cs89x0_ifdown(struct uip_driver_s *dev)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+ up_disable_irq(CONFIG_CS89x0_IRQ);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(cs89x0->cs_txpoll);
+ wd_cancel(cs89x0->cs_txtimeout);
+
+ /* Reset the device */
+
+ cs89x0->cs_bifup = false;
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: cs89x0_txavail
+ *
+ * Description:
+ * Driver callback invoked when new TX data is available. This is a
+ * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ * latency.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int cs89x0_txavail(struct uip_driver_s *dev)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (cs89x0->cs_bifup)
+ {
+ /* Check if there is room in the hardware to hold another outgoing packet. */
+#warning "Missing logic"
+
+ /* If so, then poll uIP for new XMIT data */
+
+ (void)uip_poll(&cs89x0->cs_dev, cs89x0_uiptxpoll);
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: cs89x0_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int cs89x0_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct cs89x0_driver_s *priv = (FAR struct cs89x0_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: cs89x0_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int cs89x0_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct cs89x0_driver_s *priv = (FAR struct cs89x0_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: cs89x0_initialize
+ *
+ * Description:
+ * Initialize the Ethernet driver
+ *
+ * Parameters:
+ * impl - decribes the implementation of the cs89x00 implementation.
+ * This reference is retained so so must remain stable throughout the
+ * life of the driver instance.
+ * devno - Identifies the device number. This must be a number between
+ * zero CONFIG_CS89x0_NINTERFACES and the same devno must not be
+ * initialized twice. The associated network device will be referred
+ * to with the name "eth" followed by this number (eth0, eth1, etc).
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+/* Initialize the CS89x0 chip and driver */
+
+int cs89x0_initialize(FAR const cs89x0_driver_s *cs89x0, int devno)
+{
+ /* Sanity checks -- only performed with debug enabled */
+
+#ifdef CONFIG_DEBUG
+ if (!cs89x0 || (unsigned)devno > CONFIG_CS89x0_NINTERFACES || g_cs89x00[devno])
+ {
+ return -EINVAL;
+ }
+#endif
+
+ /* Check if a Ethernet chip is recognized at its I/O base */
+
+#warning "Missing logic"
+
+ /* Attach the IRQ to the driver */
+
+ if (irq_attach(cs89x0->irq, cs89x0_interrupt))
+ {
+ /* We could not attach the ISR to the ISR */
+
+ return -EAGAIN;
+ }
+
+ /* Initialize the driver structure */
+
+ g_cs89x[devno] = cs89x0; /* Used to map IRQ back to instance */
+ cs89x0->cs_dev.d_ifup = cs89x0_ifup; /* I/F down callback */
+ cs89x0->cs_dev.d_ifdown = cs89x0_ifdown; /* I/F up (new IP address) callback */
+ cs89x0->cs_dev.d_txavail = cs89x0_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ cs89x0->cs_dev.d_addmac = cs89x0_addmac; /* Add multicast MAC address */
+ cs89x0->cs_dev.d_rmmac = cs89x0_rmmac; /* Remove multicast MAC address */
+#endif
+ cs89x0->cs_dev.d_private = (void*)cs89x0; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ cs89x0->cs_txpoll = wd_create(); /* Create periodic poll timer */
+ cs89x0->cs_txtimeout = wd_create(); /* Create TX timeout timer */
+
+ /* Read the MAC address from the hardware into cs89x0->cs_dev.d_mac.ether_addr_octet */
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ (void)netdev_register(&cs89x0->cs_dev);
+ return OK;
+}
+
+#endif /* CONFIG_NET && CONFIG_NET_CS89x0 */
+
diff --git a/nuttx/drivers/net/cs89x0.h b/nuttx/drivers/net/cs89x0.h
new file mode 100644
index 000000000..f6d99120a
--- /dev/null
+++ b/nuttx/drivers/net/cs89x0.h
@@ -0,0 +1,326 @@
+/****************************************************************************
+ * drivers/net/cs89x0.h
+ *
+ * Copyright (C) 2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_NET_CS89x0_H
+#define __DRIVERS_NET_CS89x0_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* CONFIG_CS89x0_ALIGN16/32 determines if the 16-bit CS89x0 registers are
+ * aligned to 16-bit or 32-bit address boundaries. NOTE: If there multiple
+ * CS89x00 parts in the board architecture, we assume that the address
+ * alignment is the same for all implementations. If that is not the
+ * case, then it will be necessary to move a shift value into
+ * the cs89x0_driver_s structure and calculate the offsets dynamically in
+ * the putreg and getreg functions.
+ */
+
+#if defined(CONFIG_CS89x0_ALIGN16)
+# define CS89x0_RTDATA_OFFSET (0 << 1)
+# define CS89x0_TxCMD_OFFSET (2 << 1)
+# define CS89x0_TxLEN_OFFSET (3 << 1)
+# define CS89x0_ISQ_OFFSET (4 << 1)
+# define CS89x0_PPTR_OFFSET (5 << 1)
+# define CS89x0_PDATA_OFFSET (6 << 1)
+#elif defined(CONFIG_CS89x0_ALIGN32)
+# define CS89x0_RTDATA_OFFSET (0 << 2)
+# define CS89x0_TxCMD_OFFSET (2 << 2)
+# define CS89x0_TxLEN_OFFSET (3 << 2)
+# define CS89x0_ISQ_OFFSET (4 << 2)
+# define CS89x0_PPTR_OFFSET (5 << 2)
+# define CS89x0_PDATA_OFFSET (6 << 2)
+#else
+# error "CS89x00 address alignment is not defined"
+#endif
+
+/* ISQ register bit definitions */
+
+#define ISQ_EVENTMASK 0x003f /* Bits 0-5 indicate the status register */
+#define ISQ_RXEVENT 0x0004
+#define ISQ_TXEVENT 0x0008
+#define ISQ_BUFEVENT 0x000c
+#define ISQ_RXMISSEVENT 0x0010
+#define ISQ_TXCOLEVENT 0x0012
+
+/* ISQ register TxEVENT bit definitions*/
+
+#define ISQ_RXEVENT_IAHASH (1 << 6)
+#define ISQ_RXEVENT_DRIBBLE (1 << 7)
+#define ISQ_RXEVENT_RXOK (1 << 8)
+#define ISQ_RXEVENT_HASHED (1 << 9)
+#define ISQ_RXEVENT_HASHNDX_SHIFT 10
+#define ISQ_RXEVENT_HASHNDX_MASK (0x3f << ISQ_RXEVENT_HASHNDX_SHIFT)
+
+/* ISQ register TxEVENT bit definitions*/
+
+#define ISQ_TXEVENT_LOSSOFCRS (1 << 6)
+#define ISQ_TXEVENT_SQEERROR (1 << 7)
+#define ISQ_TXEVENT_TXOK (1 << 8)
+#define ISQ_TXEVENT_OUTWINDOW (1 << 9)
+#define ISQ_TXEVENT_JABBER (1 << 10)
+#define ISQ_TXEVENT_NCOLLISION_SHIFT 11
+#define ISQ_TXEVENT_NCOLLISION_MASK (15 << ISQ_TXEVENT_NCOLLISION_SHIFT)
+#define ISQ_TXEVENT_16COLL (1 << 15)
+
+/* ISQ register BufEVENT bit definitions */
+
+#define ISQ_BUFEVENT_SWINT (1 << 6)
+#define ISQ_BUFEVENT_RXDMAFRAME (1 << 7)
+#define ISQ_BUFEVENT_RDY4TX (1 << 8)
+#define ISQ_BUFEVENT_TXUNDERRUN (1 << 9)
+#define ISQ_BUFEVENT_RXMISS (1 << 10)
+#define ISQ_BUFEVENT_RX128 (1 << 11)
+#define ISQ_BUFEVENT_RXDEST (1 << 15)
+
+/* Packet page register offsets *********************************************/
+
+/* 0x0000 Bus interface registers */
+
+#define PPR_CHIPID 0x0000 /* Chip identifier - must be 0x630E */
+#define PPR_CHIPREV 0x0002 /* Chip revision, model codes */
+#define PPR_IOBASEADDRESS 0x0020 /* I/O Base Address */
+#define PPR_INTREG 0x0022 /* Interrupt configuration */
+# define PPR_INTREG_IRQ0 0x0000 /* Use INTR0 pin */
+# define PPR_INTREG_IRQ1 0x0001 /* Use INTR1 pin */
+# define PPR_INTREG_IRQ2 0x0002 /* Use INTR2 pin */
+# define PPR_INTREG_IRQ3 0x0003 /* Use INTR3 pin */
+
+#define PPR_DMACHANNELNUMBER 0x0024 /* DMA Channel Number (0,1, or 2) */
+#define PPR_DMASTARTOFFRAME 0x0026 /* DMA Start of Frame */
+#define PPR_DMAFRAMECOUNT 0x0028 /* DMA Frame Count (12-bits) */
+#define PPR_RXDMABYTECOUNT 0x002a /* Rx DMA Byte Count */
+#define PPR_MEMORYBASEADDRESS 0x002c /* Memory Base Address Register (20-bit) */
+#define PPR_BOOTPROMBASEADDRESS 0x0030 /* Boot PROM Base Address */
+#define PPR_BOOTPROMADDRESSMASK 0x0034 /* Boot PROM Address Mask */
+#define PPR_EEPROMCOMMAND 0x0040 /* EEPROM Command */
+#define PPR_EEPROMDATA 0x0042 /* EEPROM Data */
+#define PPR_RECVFRAMEBYTES 0x0050 /* Received Frame Byte Counter */
+
+/* 0x0100 - Configuration and control registers */
+
+#define PPR_RXCFG 0x0102 /* Receiver configuration */
+# define PPR_RXCFG_SKIP1 (1 << 6) /* Skip (discard) current frame */
+# define PPR_RXCFG_STREAM (1 << 7) /* Enable streaming mode */
+# define PPR_RXCFG_RXOK (1 << 8) /* RxOK interrupt enable */
+# define PPR_RxCFG_RxDMAonly (1 << 9) /* Use RxDMA for all frames */
+# define PPR_RxCFG_AutoRxDMA (1 << 10) /* Select RxDMA automatically */
+# define PPR_RxCFG_BufferCRC (1 << 11) /* Include CRC characters in frame */
+# define PPR_RxCFG_CRC (1 << 12) /* Enable interrupt on CRC error */
+# define PPR_RxCFG_RUNT (1 << 13) /* Enable interrupt on RUNT frames */
+# define PPR_RxCFG_EXTRA (1 << 14) /* Enable interrupt on frames with extra data */
+
+#define PPR_RXCTL 0x0104 /* Receiver control */
+# define PPR_RXCTL_IAHASH (1 << 6) /* Accept frames that match hash */
+# define PPR_RXCTL_PROMISCUOUS (1 << 7) /* Accept any frame */
+# define PPR_RXCTL_RXOK (1 << 8) /* Accept well formed frames */
+# define PPR_RXCTL_MULTICAST (1 << 9) /* Accept multicast frames */
+# define PPR_RXCTL_IA (1 << 10) /* Accept frame that matches IA */
+# define PPR_RXCTL_BROADCAST (1 << 11) /* Accept broadcast frames */
+# define PPR_RXCTL_CRC (1 << 12) /* Accept frames with bad CRC */
+# define PPR_RXCTL_RUNT (1 << 13) /* Accept runt frames */
+# define PPR_RXCTL_EXTRA (1 << 14) /* Accept frames that are too long */
+
+#define PPR_TXCFG 0x0106 /* Transmit configuration */
+# define PPR_TXCFG_CRS (1 << 6) /* Enable interrupt on loss of carrier */
+# define PPR_TXCFG_SQE (1 << 7) /* Enable interrupt on Signal Quality Error */
+# define PPR_TXCFG_TXOK (1 << 8) /* Enable interrupt on successful xmits */
+# define PPR_TXCFG_LATE (1 << 9) /* Enable interrupt on "out of window" */
+# define PPR_TXCFG_JABBER (1 << 10) /* Enable interrupt on jabber detect */
+# define PPR_TXCFG_COLLISION (1 << 11) /* Enable interrupt if collision */
+# define PPR_TXCFG_16COLLISIONS (1 << 15) /* Enable interrupt if > 16 collisions */
+
+#define PPR_TXCMD 0x0108 /* Transmit command status */
+# define PPR_TXCMD_TXSTART5 (0 << 6) /* Start after 5 bytes in buffer */
+# define PPR_TXCMD_TXSTART381 (1 << 6) /* Start after 381 bytes in buffer */
+# define PPR_TXCMD_TXSTART1021 (2 << 6) /* Start after 1021 bytes in buffer */
+# define PPR_TXCMD_TXSTARTFULL (3 << 6) /* Start after all bytes loaded */
+# define PPR_TXCMD_FORCE (1 << 8) /* Discard any pending packets */
+# define PPR_TXCMD_ONECOLLISION (1 << 9) /* Abort after a single collision */
+# define PPR_TXCMD_NOCRC (1 << 12) /* Do not add CRC */
+# define PPR_TXCMD_NOPAD (1 << 13) /* Do not pad short packets */
+
+#define PPR_BUFCFG 0x010a /* Buffer configuration */
+# define PPR_BUFCFG_SWI (1 << 6) /* Force interrupt via software */
+# define PPR_BUFCFG_RXDMA (1 << 7) /* Enable interrupt on Rx DMA */
+# define PPR_BUFCFG_TXRDY (1 << 8) /* Enable interrupt when ready for Tx */
+# define PPR_BUFCFG_TXUE (1 << 9) /* Enable interrupt in Tx underrun */
+# define PPR_BUFCFG_RXMISS (1 << 10) /* Enable interrupt on missed Rx packets */
+# define PPR_BUFCFG_RX128 (1 << 11) /* Enable Rx interrupt after 128 bytes */
+# define PPR_BUFCFG_TXCOL (1 << 12) /* Enable int on Tx collision ctr overflow */
+# define PPR_BUFCFG_MISS (1 << 13) /* Enable int on Rx miss ctr overflow */
+# define PPR_BUFCFG_RXDEST (1 << 15) /* Enable int on Rx dest addr match */
+
+#define PPR_LINECTL 0x0112 /* Line control */
+# define PPR_LINECTL_RX (1 << 6) /* Enable receiver */
+# define PPR_LINECTL_TX (1 << 7) /* Enable transmitter */
+# define PPR_LINECTL_AUIONLY (1 << 8) /* AUI interface only */
+# define PPR_LINECTL_AUTOAUI10BT (1 << 9) /* Autodetect AUI or 10BaseT interface */
+# define PPR_LINECTL_MODBACKOFFE (1 << 11) /* Enable modified backoff algorithm */
+# define PPR_LINECTL_POLARITYDIS (1 << 12) /* Disable Rx polarity autodetect */
+# define PPR_LINECTL_2PARTDEFDIS (1 << 13) /* Disable two-part defferal */
+# define PPR_LINECTL_LORXSQUELCH (1 << 14) /* Reduce receiver squelch threshold */
+
+#define PPR_SELFCTL 0x0114 /* Chip self control */
+# define PPR_SELFCTL_RESET (1 << 6) /* Self-clearing reset */
+# define PPR_SELFCTL_SWSUSPEND (1 << 8) /* Initiate suspend mode */
+# define PPR_SELFCTL_HWSLEEPE (1 << 9) /* Enable SLEEP input */
+# define PPR_SELFCTL_HWSTANDBYE (1 << 10) /* Enable standby mode */
+# define PPR_SELFCTL_HC0E (1 << 12) /* Use HCB0 for LINK LED */
+# define PPR_SELFCTL_HC1E (1 << 13) /* Use HCB1 for BSTATUS LED */
+# define PPR_SELFCTL_HCB0 (1 << 14) /* Control LINK LED if HC0E set */
+# define PPR_SELFCTL_HCB1 (1 << 15) /* Cntrol BSTATUS LED if HC1E set */
+
+#define PPR_BUSCTL 0x0116 /* Bus control */
+# define PPR_BUSCTL_RESETRXDMA (1 << 6) /* Reset RxDMA pointer */
+# define PPR_BUSCTL_DMAEXTEND (1 << 8) /* Extend DMA cycle */
+# define PPR_BUSCTL_USESA (1 << 9) /* Assert MEMCS16 on address decode */
+# define PPR_BUSCTL_MEMORYE (1 << 10) /* Enable memory mode */
+# define PPR_BUSCTL_DMABURST (1 << 11) /* Limit DMA access burst */
+# define PPR_BUSCTL_IOCHRDYE (1 << 12) /* Set IOCHRDY high impedence */
+# define PPR_BUSCTL_RXDMASIZE (1 << 13) /* Set DMA buffer size 64KB */
+# define PPR_BUSCTL_ENABLEIRQ (1 << 15) /* Generate interrupt on interrupt event */
+
+#define PPR_TESTCTL 0x0118 /* Test control */
+# define PPR_TESTCTL_DISABLELT (1 << 7) /* Disable link status */
+# define PPR_TESTCTL_ENDECLOOP (1 << 9) /* Internal loopback */
+# define PPR_TESTCTL_AUILOOP (1 << 10) /* AUI loopback */
+# define PPR_TESTCTL_DISBACKOFF (1 << 11) /* Disable backoff algorithm */
+# define PPR_TESTCTL_FDX (1 << 14) /* Enable full duplex mode */
+
+/* 0x0120 - Status and Event Registers */
+
+#define PPR_ISQ 0x0120 /* Interrupt Status Queue */
+#define PPR_RER 0x0124 /* Receive event */
+# define PPR_RER_IAHASH (1 << 6) /* Frame hash match */
+# define PPR_RER_DRIBBLE (1 << 7) /* Frame had 1-7 extra bits after last byte */
+# define PPR_RER_RXOK (1 << 8) /* Frame received with no errors */
+# define PPR_RER_HASHED (1 << 9) /* Frame address hashed OK */
+# define PPR_RER_IA (1 << 10) /* Frame address matched IA */
+# define PPR_RER_BROADCAST (1 << 11) /* Broadcast frame */
+# define PPR_RER_CRC (1 << 12) /* Frame had CRC error */
+# define PPR_RER_RUNT (1 << 13) /* Runt frame */
+# define PPR_RER_EXTRA (1 << 14) /* Frame was too long */
+
+#define PPR_TER 0x0128 /* Transmit event */
+# define PPR_TER_CRS (1 << 6) /* Carrier lost */
+# define PPR_TER_SQE (1 << 7) /* Signal Quality Error */
+# define PPR_TER_TXOK (1 << 8) /* Packet sent without error */
+# define PPR_TER_LATE (1 << 9) /* Out of window */
+# define PPR_TER_JABBER (1 << 10) /* Stuck transmit? */
+# define PPR_TER_NUMCOLLISIONS_SHIFT 11
+# define PPR_TER_NUMCOLLISIONS_MASK (15 << PPR_TER_NUMCOLLISIONS_SHIFT)
+# define PPR_TER_16COLLISIONS (1 << 15) /* > 16 collisions */
+
+#define PPR_BER 0x012C /* Buffer event */
+# define PPR_BER_SWINT (1 << 6) /* Software interrupt */
+# define PPR_BER_RXDMAFRAME (1 << 7) /* Received framed DMAed */
+# define PPR_BER_RDY4TX (1 << 8) /* Ready for transmission */
+# define PPR_BER_TXUNDERRUN (1 << 9) /* Transmit underrun */
+# define PPR_BER_RXMISS (1 << 10) /* Received frame missed */
+# define PPR_BER_RX128 (1 << 11) /* 128 bytes received */
+# define PPR_BER_RXDEST (1 << 15) /* Received framed passed address filter */
+
+#define PPR_RXMISS 0x0130 /* Receiver miss counter */
+#define PPR_TXCOL 0x0132 /* Transmit collision counter */
+#define PPR_LINESTAT 0x0134 /* Line status */
+# define PPR_LINESTAT_LINKOK (1 << 7) /* Line is connected and working */
+# define PPR_LINESTAT_AUI (1 << 8) /* Connected via AUI */
+# define PPR_LINESTAT_10BT (1 << 9) /* Connected via twisted pair */
+# define PPR_LINESTAT_POLARITY (1 << 12) /* Line polarity OK (10BT only) */
+# define PPR_LINESTAT_CRS (1 << 14) /* Frame being received */
+
+#define PPR_SELFSTAT 0x0136 /* Chip self status */
+# define PPR_SELFSTAT_33VACTIVE (1 << 6) /* supply voltage is 3.3V */
+# define PPR_SELFSTAT_INITD (1 << 7) /* Chip initialization complete */
+# define PPR_SELFSTAT_SIBSY (1 << 8) /* EEPROM is busy */
+# define PPR_SELFSTAT_EEPROM (1 << 9) /* EEPROM present */
+# define PPR_SELFSTAT_EEPROMOK (1 << 10) /* EEPROM checks out */
+# define PPR_SELFSTAT_ELPRESENT (1 << 11) /* External address latch logic available */
+# define PPR_SELFSTAT_EESIZE (1 << 12) /* Size of EEPROM */
+
+#define PPR_BUSSTAT 0x0138 /* Bus status */
+# define PPR_BUSSTAT_TXBID (1 << 7) /* Tx error */
+# define PPR_BUSSTAT_TXRDY (1 << 8) /* Ready for Tx data */
+
+#define PPR_TDR 0x013C /* AUI Time Domain Reflectometer */
+
+/* 0x0144 - Initiate transmit registers */
+
+#define PPR_TXCOMMAND 0x0144 /* Tx Command */
+#define PPR_TXLENGTH 0x0146 /* Tx Length */
+
+/* 0x0150 - Address filter registers */
+
+#define PPR_LAF 0x0150 /* Logical address filter (6 bytes) */
+#define PPR_IA 0x0158 /* Individual address (MAC) */
+
+/* 0x0400 - Frame location registers */
+
+#define PPR_RXSTATUS 0x0400 /* Rx Status */
+#define PPR_RXLENGTH 0x0402 /* Rx Length */
+#define PPR_RXFRAMELOCATION 0x0404 /* Rx Frame Location */
+#define PPR_TXFRAMELOCATION 0x0a00 /* Tx Frame Location */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRIVERS_NET_CS89x0_H */
diff --git a/nuttx/drivers/net/dm90x0.c b/nuttx/drivers/net/dm90x0.c
new file mode 100644
index 000000000..2f5b26abb
--- /dev/null
+++ b/nuttx/drivers/net/dm90x0.c
@@ -0,0 +1,1815 @@
+/****************************************************************************
+ * drivers/net/dm9x.c
+ *
+ * Copyright (C) 2007-2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Davicom data sheets (DM9000-DS-F03-041906.pdf,
+ * DM9010-DS-F01-103006.pdf) and looking at lots of other DM90x0
+ * drivers.
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#if defined(CONFIG_NET) && defined(CONFIG_NET_DM90x0)
+
+/* Only one hardware interface supported at present (although there are
+ * hooks throughout the design to that extending the support to multiple
+ * interfaces should not be that difficult)
+ */
+
+#undef CONFIG_DM9X_NINTERFACES
+#define CONFIG_DM9X_NINTERFACES 1
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <net/ethernet.h>
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* DM90000 and DM9010 register offets */
+
+#define DM9X_NETC 0x00 /* Network control register */
+#define DM9X_NETS 0x01 /* Network Status register */
+#define DM9X_TXC 0x02 /* TX control register */
+#define DM9X_TXS1 0x03 /* TX status register 1 */
+#define DM9X_TXS2 0x03 /* TX status register 2 */
+#define DM9X_RXC 0x05 /* RX control register */
+#define DM9X_RXS 0x06 /* RX status register */
+#define DM9X_RXOVF 0x07 /* Receive overflow counter register */
+#define DM9X_BPTHRES 0x08 /* Back pressure threshold register */
+#define DM9X_FCTHRES 0x09 /* Flow control threshold register */
+#define DM9X_FC 0x0a /* RX/TX flow control register */
+#define DM9X_EEPHYC 0x0b /* EEPROM & PHY control register */
+#define DM9X_EEPHYA 0x0c /* EEPROM & PHY address register */
+#define DM9X_EEPHYDL 0x0d /* EEPROM & PHY data register (lo) */
+#define DM9X_EEPHYDH 0x0e /* EEPROM & PHY data register (hi) */
+#define DM9X_WAKEUP 0x0f /* Wake-up control register */
+#define DM9X_PAB0 0x10 /* Physical address register (byte 0) */
+#define DM9X_PAB1 0x11 /* Physical address register (byte 1) */
+#define DM9X_PAB2 0x12 /* Physical address register (byte 2) */
+#define DM9X_PAB3 0x13 /* Physical address register (byte 3) */
+#define DM9X_PAB4 0x14 /* Physical address register (byte 4) */
+#define DM9X_PAB5 0x15 /* Physical address register (byte 5) */
+#define DM9X_MAB0 0x16 /* Multicast address register (byte 0) */
+#define DM9X_MAB1 0x17 /* Multicast address register (byte 1) */
+#define DM9X_MAB2 0x18 /* Multicast address register (byte 2) */
+#define DM9X_MAB3 0x19 /* Multicast address register (byte 3) */
+#define DM9X_MAB4 0x1a /* Multicast address register (byte 4) */
+#define DM9X_MAB5 0x1b /* Multicast address register (byte 5) */
+#define DM9X_MAB6 0x1c /* Multicast address register (byte 6) */
+#define DM9X_MAB7 0x1d /* Multicast address register (byte 7) */
+#define DM9X_GPC 0x1e /* General purpose control register */
+#define DM9X_GPD 0x1f /* General purpose register */
+
+#define DM9X_TRPAL 0x22 /* TX read pointer address (lo) */
+#define DM9X_TRPAH 0x23 /* TX read pointer address (hi) */
+#define DM9X_RWPAL 0x24 /* RX write pointer address (lo) */
+#define DM9X_RWPAH 0x25 /* RX write pointer address (hi) */
+
+#define DM9X_VIDL 0x28 /* Vendor ID (lo) */
+#define DM9X_VIDH 0x29 /* Vendor ID (hi) */
+#define DM9X_PIDL 0x2a /* Product ID (lo) */
+#define DM9X_PIDH 0x2b /* Product ID (hi) */
+#define DM9X_CHIPR 0x2c /* Product ID (lo) */
+#define DM9X_TXC2 0x2d /* Transmit control register 2 (dm9010) */
+#define DM9X_OTC 0x2e /* Operation test control register (dm9010) */
+#define DM9X_SMODEC 0x2f /* Special mode control register */
+#define DM9X_ETXCSR 0x30 /* Early transmit control/status register (dm9010) */
+#define DM9X_TCCR 0x31 /* Transmit checksum control register (dm9010) */
+#define DM9X_RCSR 0x32 /* Receive checksum control/status register (dm9010) */
+#define DM9X_EPHYA 0x33 /* External PHY address register (dm9010) */
+#define DM9X_GPC2 0x34 /* General purpose control register 2 (dm9010) */
+#define DM9X_GPD2 0x35 /* General purpose register 2 */
+#define DM9X_GPC3 0x36 /* General purpose control register 3 (dm9010) */
+#define DM9X_GPD3 0x37 /* General purpose register 3 */
+#define DM9X_PBUSC 0x38 /* Processor bus control register (dm9010) */
+#define DM9X_IPINC 0x39 /* INT pin control register (dm9010) */
+
+#define DM9X_MON1 0x40 /* Monitor register 1 (dm9010) */
+#define DM9X_MON2 0x41 /* Monitor register 2 (dm9010) */
+
+#define DM9X_SCLKC 0x50 /* System clock turn ON control register (dm9010) */
+#define DM9X_SCLKR 0x51 /* Resume system clock control register (dm9010) */
+
+#define DM9X_MRCMDX 0xf0 /* Memory data pre-fetch read command without address increment */
+#define DM9X_MRCMDX1 0xf1 /* memory data read command without address increment (dm9010) */
+#define DM9X_MRCMD 0xf2 /* Memory data read command with address increment */
+#define DM9X_MDRAL 0xf4 /* Memory data read address register (lo) */
+#define DM9X_MDRAH 0xf5 /* Memory data read address register (hi) */
+#define DM9X_MWCMDX 0xf6 /* Memory data write command without address increment */
+#define DM9X_MWCMD 0xf8 /* Memory data write command with address increment */
+#define DM9X_MDWAL 0xfa /* Memory data write address register (lo) */
+#define DM9X_MDWAH 0xfb /* Memory data write address register (lo) */
+#define DM9X_TXPLL 0xfc /* Memory data write address register (lo) */
+#define DM9X_TXPLH 0xfd /* Memory data write address register (hi) */
+#define DM9X_ISR 0xfe /* Interrupt status register */
+#define DM9X_IMR 0xff /* Interrupt mask register */
+
+/* Network control register bit definitions */
+
+#define DM9X_NETC_RST (1 << 0) /* Software reset */
+#define DM9X_NETC_LBKM (3 << 1) /* Loopback mode mask */
+#define DM9X_NETC_LBK0 (0 << 1) /* 0: Normal */
+#define DM9X_NETC_LBK1 (1 << 1) /* 1: MAC internal loopback */
+#define DM9X_NETC_LBK2 (2 << 1) /* 2: Internal PHY 100M mode loopback */
+#define DM9X_NETC_FDX (1 << 3) /* Full dupliex mode */
+#define DM9X_NETC_FCOL (1 << 4) /* Force collision mode */
+#define DM9X_NETC_WAKEEN (1 << 6) /* Wakeup event enable */
+#define DM9X_NETC_EXTPHY (1 << 7) /* Select external PHY */
+
+/* Network status bit definitions */
+
+#define DM9X_NETS_RXOV (1 << 1) /* RX Fifo overflow */
+#define DM9X_NETS_TX1END (1 << 2) /* TX packet 1 complete status */
+#define DM9X_NETS_TX2END (1 << 3) /* TX packet 2 complete status */
+#define DM9X_NETS_WAKEST (1 << 5) /* Wakeup event status */
+#define DM9X_NETS_LINKST (1 << 6) /* Link status */
+#define DM9X_NETS_SPEED (1 << 7) /* Media speed */
+
+/* IMR/ISR bit definitions */
+
+#define DM9X_INT_PR (1 << 0) /* Packet received interrupt */
+#define DM9X_INT_PT (1 << 1) /* Packet transmitted interrupt */
+#define DM9X_INT_RO (1 << 2) /* Receive overflow interrupt */
+#define DM9X_INT_ROO (1 << 3) /* Receive overflow counter overflow int */
+#define DM9X_INT_UDRUN (1 << 4) /* Transmit underrun interrupt */
+#define DM9X_INT_LNKCHG (1 << 5) /* Link status change interrupt */
+#define DM9X_INT_ALL (0x3f)
+
+#define DM9X_IMR_UNUSED (1 << 6) /* (not used) */
+#define DM9X_IMR_PAR (1 << 7) /* Enable auto R/W pointer reset */
+
+#define DM9X_ISR_IOMODEM (3 << 6) /* IO mode mask */
+#define DM9X_ISR_IOMODE8 (2 << 6) /* IO mode = 8 bit */
+#define DM9X_ISR_IOMODE16 (0 << 6) /* IO mode = 16 bit */
+#define DM9X_ISR_IOMODE32 (1 << 6) /* IO mode = 32 bit */
+
+#define DM9X_IMRENABLE (DM9X_INT_PR|DM9X_INT_PT|DM9X_INT_LNKCHG|DM9X_IMR_PAR)
+#define DM9X_IMRRXDISABLE (DM9X_INT_PT|DM9X_INT_LNKCHG|DM9X_IMR_PAR)
+#define DM9X_IMRDISABLE (DM9X_IMR_PAR)
+
+/* EEPROM/PHY control regiser bits */
+
+#define DM9X_EEPHYC_ERRE (1 << 0) /* EEPROM (vs PHY) access status */
+#define DM9X_EEPHYC_ERPRW (1 << 1) /* EEPROM/PHY write access */
+#define DM9X_EEPHYC_ERPRR (1 << 2) /* EEPROM/PHY read access */
+#define DM9X_EEPHYC_EPOS (1 << 3) /* EEPROM/PHY operation select */
+#define DM9X_EEPHYC_WEP (1 << 4) /* Write EEPROM enable */
+#define DM9X_EEPHYC_REEP (1 << 5) /* Reload EEPROM */
+
+/* Supported values from the vendor and product ID register */
+
+#define DM9X_DAVICOMVID 0x0a46
+#define DM9X_DM9000PID 0x9000
+#define DM9X_DM9010PID 0x9010
+
+/* RX control register bit settings */
+
+#define DM9X_RXC_RXEN (1 << 0) /* RX enable */
+#define DM9X_RXC_PRMSC (1 << 1) /* Promiscuous mode */
+#define DM9X_RXC_RUNT (1 << 2) /* Pass runt packet */
+#define DM9X_RXC_ALL (1 << 3) /* Pass all multicast */
+#define DM9X_RXC_DISCRC (1 << 4) /* Discard CRC error packets */
+#define DM9X_RXC_DISLONG (1 << 5) /* Discard long packets */
+#define DM9X_RXC_WTDIS (1 << 6) /* Disable watchdog timer */
+#define DM9X_RXC_HASHALL (1 << 7) /* Filter all addresses in hash table */
+
+#define DM9X_RXCSETUP (DM9X_RXC_DISCRC|DM9X_RXC_DISLONG)
+
+/* EEPHY bit settings */
+
+#define DM9X_EEPHYA_EROA 0x40 /* PHY register address 0x01 */
+
+#define DM9X_PKTRDY 0x01 /* Packet ready to receive */
+
+/* The RX interrupt will be disabled if more than the following RX
+ * interrupts are received back-to-back.
+ */
+
+#define DM9X_CRXTHRES 10
+
+/* All access is via an index register and a data regist. Select accecss
+ * according to user supplied base address and bus width.
+ */
+
+#if defined(CONFIG_DM9X_BUSWIDTH8)
+# define DM9X_INDEX *(volatile uint8_t*)(CONFIG_DM9X_BASE)
+# define DM9X_DATA *(volatile uint8_t*)(CONFIG_DM9X_BASE + 2)
+#elif defined(CONFIG_DM9X_BUSWIDTH16)
+# define DM9X_INDEX *(volatile uint16_t*)(CONFIG_DM9X_BASE)
+# define DM9X_DATA *(volatile uint16_t*)(CONFIG_DM9X_BASE + 2)
+#elif defined(CONFIG_DM9X_BUSWIDTH32)
+# define DM9X_INDEX *(volatile uint32_t*)(CONFIG_DM9X_BASE)
+# define DM9X_DATA *(volatile uint32_t*)(CONFIG_DM9X_BASE + 2)
+#endif
+
+/* Phy operating mode. Default is AUTO, but this setting can be overridden
+ * in the NuttX configuration file.
+ */
+
+#define DM9X_MODE_AUTO 0
+#define DM9X_MODE_10MHD 1
+#define DM9X_MODE_100MHD 2
+#define DM9X_MODE_10MFD 3
+#define DM9X_MODE_100MFD 4
+
+#ifndef CONFIG_DM9X_MODE
+# define CONFIG_DM9X_MODE DM9X_MODE_AUTO
+#endif
+
+/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define DM6X_WDDELAY (1*CLK_TCK)
+#define DM6X_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define DM6X_TXTIMEOUT (60*CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)dm9x->dm_dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+union rx_desc_u
+{
+ uint8_t rx_buf[4];
+ struct
+ {
+ uint8_t rx_byte;
+ uint8_t rx_status;
+ uint16_t rx_len;
+ } desc;
+};
+
+/* The dm9x_driver_s encapsulates all DM90x0 state information for a single
+ * DM90x0 hardware interface
+ */
+
+struct dm9x_driver_s
+{
+ bool dm_bifup; /* true:ifup false:ifdown */
+ bool dm_b100M; /* true:speed == 100M; false:speed == 10M */
+ WDOG_ID dm_txpoll; /* TX poll timer */
+ WDOG_ID dm_txtimeout; /* TX timeout timer */
+ uint8_t dm_ntxpending; /* Count of packets pending transmission */
+ uint8_t ncrxpackets; /* Number of continuous rx packets */
+
+ /* Mode-dependent function to move data in 8/16/32 I/O modes */
+
+ void (*dm_read)(uint8_t *ptr, int len);
+ void (*dm_write)(const uint8_t *ptr, int len);
+ void (*dm_discard)(int len);
+
+#if defined(CONFIG_DM9X_STATS)
+ uint32_t dm_ntxpackets; /* Count of packets sent */
+ uint32_t dm_ntxbytes; /* Count of bytes sent */
+ uint32_t dm_ntxerrors; /* Count of TX errors */
+ uint32_t dm_nrxpackets; /* Count of packets received */
+ uint32_t dm_nrxbytes; /* Count of bytes received */
+ uint32_t dm_nrxfifoerrors; /* Count of RX FIFO overflow errors */
+ uint32_t dm_nrxcrcerrors; /* Count of RX CRC errors */
+ uint32_t dm_nrxlengtherrors; /* Count of RX length errors */
+ uint32_t dm_nphyserrors; /* Count of physical layer errors */
+ uint32_t dm_nresets; /* Counts number of resets */
+ uint32_t dm_ntxtimeouts; /* Counts resets caused by TX timeouts */
+#endif
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s dm_dev;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* At present, only a single DM90x0 device is supported. */
+
+static struct dm9x_driver_s g_dm9x[CONFIG_DM9X_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Utility functions */
+
+static uint8_t getreg(int reg);
+static void putreg(int reg, uint8_t value);
+static void read8(uint8_t *ptr, int len);
+static void read16(uint8_t *ptr, int len);
+static void read32(uint8_t *ptr, int len);
+static void discard8(int len);
+static void discard16(int len);
+static void discard32(int len);
+static void write8(const uint8_t *ptr, int len);
+static void write16(const uint8_t *ptr, int len);
+static void write32(const uint8_t *ptr, int len);
+
+/* static uint16_t dm9x_readsrom(struct dm9x_driver_s *dm9x, int offset); */
+static uint16_t dm9x_phyread(struct dm9x_driver_s *dm9x, int reg);
+static void dm9x_phywrite(struct dm9x_driver_s *dm9x, int reg, uint16_t value);
+
+#if defined(CONFIG_DM9X_STATS)
+static void dm9x_resetstatistics(struct dm9x_driver_s *dm9x);
+#else
+# define dm9x_resetstatistics(dm9x)
+#endif
+
+#if defined(CONFIG_DM9X_STATS) && defined(CONFIG_DEBUG)
+static void dm9x_dumpstatistics(struct dm9x_driver_s *dm9x);
+#else
+# define dm9x_dumpstatistics(dm9x)
+#endif
+
+#if defined(CONFIG_DM9X_CHECKSUM)
+static bool dm9x_rxchecksumready(uint8_t);
+#else
+# define dm9x_rxchecksumready(a) ((a) == 0x01)
+#endif
+
+/* Common TX logic */
+
+static int dm9x_transmit(struct dm9x_driver_s *dm9x);
+static int dm9x_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void dm9x_receive(struct dm9x_driver_s *dm9x);
+static void dm9x_txdone(struct dm9x_driver_s *dm9x);
+static int dm9x_interrupt(int irq, FAR void *context);
+
+/* Watchdog timer expirations */
+
+static void dm9x_polltimer(int argc, uint32_t arg, ...);
+static void dm9x_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int dm9x_ifup(struct uip_driver_s *dev);
+static int dm9x_ifdown(struct uip_driver_s *dev);
+static int dm9x_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int dm9x_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int dm9x_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/* Initialization functions */
+
+static void dm9x_bringup(struct dm9x_driver_s *dm9x);
+static void dm9x_reset(struct dm9x_driver_s *dm9x);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: getreg and setreg
+ *
+ * Description:
+ * Access to memory-mapped DM90x0 8-bit registers
+ *
+ * Parameters:
+ * reg - Register number
+ * value - Value to write to the register (setreg only)
+ *
+ * Returned Value:
+ * Value read from the register (getreg only)
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint8_t getreg(int reg)
+{
+ DM9X_INDEX = reg;
+ return DM9X_DATA & 0xff;
+}
+
+static void putreg(int reg, uint8_t value)
+{
+ DM9X_INDEX = reg;
+ DM9X_DATA = value & 0xff;
+}
+
+/****************************************************************************
+ * Function: read8, read16, read32
+ *
+ * Description:
+ * Read packet data from the DM90x0 SRAM based on its current I/O mode
+ *
+ * Parameters:
+ * ptr - Location to write the packet data
+ * len - The number of bytes to read
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void read8(uint8_t *ptr, int len)
+{
+ nvdbg("Read %d bytes (8-bit mode)\n", len);
+ for (; len > 0; len--)
+ {
+ *ptr++ = DM9X_DATA;
+ }
+}
+
+static void read16(uint8_t *ptr, int len)
+{
+ register uint16_t *ptr16 = (uint16_t*)ptr;
+ nvdbg("Read %d bytes (16-bit mode)\n", len);
+ for (; len > 0; len -= sizeof(uint16_t))
+ {
+ *ptr16++ = DM9X_DATA;
+ }
+}
+
+static void read32(uint8_t *ptr, int len)
+{
+ register uint32_t *ptr32 = (uint32_t*)ptr;
+ nvdbg("Read %d bytes (32-bit mode)\n", len);
+ for (; len > 0; len -= sizeof(uint32_t))
+ {
+ *ptr32++ = DM9X_DATA;
+ }
+}
+
+/****************************************************************************
+ * Function: discard8, discard16, discard32
+ *
+ * Description:
+ * Read and discard packet data in the DM90x0 SRAM based on its current
+ * I/O mode
+ *
+ * Parameters:
+ * len - The number of bytes to discard
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void discard8(int len)
+{
+ nvdbg("Discard %d bytes (8-bit mode)\n", len);
+ for (; len > 0; len--)
+ {
+ DM9X_DATA;
+ }
+}
+
+static void discard16(int len)
+{
+ nvdbg("Discard %d bytes (16-bit mode)\n", len);
+ for (; len > 0; len -= sizeof(uint16_t))
+ {
+ DM9X_DATA;
+ }
+}
+
+static void discard32(int len)
+{
+ nvdbg("Discard %d bytes (32-bit mode)\n", len);
+ for (; len > 0; len -= sizeof(uint32_t))
+ {
+ DM9X_DATA;
+ }
+}
+
+/****************************************************************************
+ * Function: write8, write16, write32
+ *
+ * Description:
+ * Write packet data into the DM90x0 SRAM based on its current I/O mode
+ *
+ * Parameters:
+ * ptr - Location to write the packet data
+ * len - The number of bytes to read
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void write8(const uint8_t *ptr, int len)
+{
+ nvdbg("Write %d bytes (8-bit mode)\n", len);
+ for (; len > 0; len--)
+ {
+ DM9X_DATA = (*ptr++ & 0xff);
+ }
+}
+
+static void write16(const uint8_t *ptr, int len)
+{
+ register uint16_t *ptr16 = (uint16_t*)ptr;
+ nvdbg("Write %d bytes (16-bit mode)\n", len);
+
+ for (; len > 0; len -= sizeof(uint16_t))
+ {
+ DM9X_DATA = *ptr16++;
+ }
+}
+
+static void write32(const uint8_t *ptr, int len)
+{
+ register uint32_t *ptr32 = (uint32_t*)ptr;
+ nvdbg("Write %d bytes (32-bit mode)\n", len);
+ for (; len > 0; len -= sizeof(uint32_t))
+ {
+ DM9X_DATA = *ptr32++;
+ }
+}
+
+/****************************************************************************
+ * Function: dm9x_readsrom
+ *
+ * Description:
+ * Read a word from SROM
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ * offset - SROM offset to read from
+ *
+ * Returned Value:
+ * SROM content at that offset
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if 0 /* Not used */
+static uint16_t dm9x_readsrom(struct dm9x_driver_s *dm9x, int offset)
+{
+ putreg(DM9X_EEPHYA, offset);
+ putreg(DM9X_EEPHYC, DM9X_EEPHYC_ERPRR);
+ up_udelay(200);
+ putreg(DM9X_EEPHYC, 0x00);
+ return (getreg(DM9X_EEPHYDL) + (getreg(DM9X_EEPHYDH) << 8) );
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_phyread and dm9x_phywrite
+ *
+ * Description:
+ * Read/write data from/to the PHY
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ * reg - PHY register offset
+ * value - The value to write to the PHY register (dm9x_write only)
+ *
+ * Returned Value:
+ * The value read from the PHY (dm9x_read only)
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint16_t dm9x_phyread(struct dm9x_driver_s *dm9x, int reg)
+{
+ /* Setup DM9X_EEPHYA, the EEPROM/PHY address register */
+
+ putreg(DM9X_EEPHYA, DM9X_EEPHYA_EROA | reg);
+
+ /* Issue PHY read command pulse in the EEPROM/PHY control register */
+
+ putreg(DM9X_EEPHYC, (DM9X_EEPHYC_ERPRR|DM9X_EEPHYC_EPOS));
+ up_udelay(100);
+ putreg(DM9X_EEPHYC, 0x00);
+
+ /* Return the data from the EEPROM/PHY data register pair */
+
+ return (((uint16_t)getreg(DM9X_EEPHYDH)) << 8) | (uint16_t)getreg(DM9X_EEPHYDL);
+}
+
+static void dm9x_phywrite(struct dm9x_driver_s *dm9x, int reg, uint16_t value)
+{
+ /* Setup DM9X_EEPHYA, the EEPROM/PHY address register */
+
+ putreg(DM9X_EEPHYA, DM9X_EEPHYA_EROA | reg);
+
+ /* Put the data to write in the EEPROM/PHY data register pair */
+
+ putreg(DM9X_EEPHYDL, (value & 0xff));
+ putreg(DM9X_EEPHYDH, ((value >> 8) & 0xff));
+
+ /* Issue PHY write command pulse in the EEPROM/PHY control register */
+
+ putreg(DM9X_EEPHYC, (DM9X_EEPHYC_ERPRW|DM9X_EEPHYC_EPOS));
+ up_udelay(500);
+ putreg(DM9X_EEPHYC, 0x0);
+}
+
+/****************************************************************************
+ * Function: dm9x_resetstatistics
+ *
+ * Description:
+ * Reset all DM90x0 statistics
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DM9X_STATS)
+static void dm9x_resetstatistics(struct dm9x_driver_s *dm9x)
+{
+ dm9x->dm_ntxpackets = 0; /* Count of packets sent */
+ dm9x->dm_ntxbytes = 0; /* Count of bytes sent */
+ dm9x->dm_ntxerrors = 0; /* Count of TX errors */
+ dm9x->dm_nrxpackets = 0; /* Count of packets received */
+ dm9x->dm_nrxbytes = 0; /* Count of bytes received */
+ dm9x->dm_nrxfifoerrors = 0; /* Count of RX FIFO overflow errors */
+ dm9x->dm_nrxcrcerrors = 0; /* Count of RX CRC errors */
+ dm9x->dm_nrxlengtherrors = 0; /* Count of RX length errors */
+ dm9x->dm_nphyserrors = 0; /* Count of physical layer errors */
+ dm9x->dm_nresets = 0; /* Counts number of resets */
+ dm9x->dm_ntxtimeouts = 0; /* Counts resets caused by TX timeouts */
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_dumpstatistics
+ *
+ * Description:
+ * Print the current value of all DM90x0 statistics
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DM9X_STATS) && defined(CONFIG_DEBUG)
+static void dm9x_dumpstatistics(struct dm9x_driver_s *dm9x)
+{
+ ndbg("TX packets: %d\n", dm9x->dm_ntxpackets);
+ ndbg(" bytes: %d\n", dm9x->dm_ntxbytes);
+ ndbg(" errors: %d\n", dm9x->dm_ntxerrors);
+ ndbg("RX packets: %d\n", dm9x->dm_nrxpackets);
+ ndbg(" bytes: %d\n", dm9x->dm_nrxbytes);
+ ndbg(" FIFO overflows: %d\n", dm9x->dm_nrxfifoerrors);
+ ndbg(" CRC errors: %d\n", dm9x->dm_nrxcrcerrors);
+ ndbg(" length errors: %d\n", dm9x->dm_nrxlengtherrors);
+ ndbg("Physical layer errors: %d\n", dm9x->dm_nphyserrors);
+ ndbg("Resets: %d\n", dm9x->dm_nresets);
+ ndbg("TX timeout resets: %d\n", dm9x->dm_ntxtimeouts);
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_rxchecksumready
+ *
+ * Description:
+ * Return true if the RX checksum is available
+ *
+ * Parameters:
+ * rxbyte
+ *
+ * Returned Value:
+ * true: checksum is ready
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DM9X_CHECKSUM)
+static inline bool dm9x_rxchecksumready(uint8_t rxbyte)
+{
+ if ((rxbyte & 0x01) == 0)
+ {
+ return false;
+ }
+
+ return ((rxbyte >> 4) | 0x01) != 0;
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int dm9x_transmit(struct dm9x_driver_s *dm9x)
+{
+ /* Check if there is room in the DM90x0 to hold another packet. In 100M mode,
+ * that can be 2 packets, otherwise it is a single packet.
+ */
+
+ if (dm9x->dm_ntxpending < 1 || (dm9x->dm_b100M && dm9x->dm_ntxpending < 2))
+ {
+ /* Increment count of packets transmitted */
+
+ dm9x->dm_ntxpending++;
+#if defined(CONFIG_DM9X_STATS)
+ dm9x->dm_ntxpackets++;
+ dm9x->dm_ntxbytes += dm9x->dm_dev.d_len;
+#endif
+
+ /* Disable all DM90x0 interrupts */
+
+ putreg(DM9X_IMR, DM9X_IMRDISABLE);
+
+ /* Set the TX length */
+
+ putreg(DM9X_TXPLL, (dm9x->dm_dev.d_len & 0xff));
+ putreg(DM9X_TXPLH, (dm9x->dm_dev.d_len >> 8) & 0xff);
+
+ /* Move the data to be sent into TX SRAM */
+
+ DM9X_INDEX = DM9X_MWCMD;
+ dm9x->dm_write(dm9x->dm_dev.d_buf, dm9x->dm_dev.d_len);
+
+#if !defined(CONFIG_DM9X_ETRANS)
+ /* Issue TX polling command */
+
+ putreg(DM9X_TXC, 0x1); /* Cleared after TX complete*/
+#endif
+
+ /* Clear count of back-to-back RX packet transfers */
+
+ dm9x->ncrxpackets = 0;
+
+ /* Re-enable DM90x0 interrupts */
+
+ putreg(DM9X_IMR, DM9X_IMRENABLE);
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ (void)wd_start(dm9x->dm_txtimeout, DM6X_TXTIMEOUT, dm9x_txtimeout, 1, (uint32_t)dm9x);
+ return OK;
+ }
+ return -EBUSY;
+}
+
+/****************************************************************************
+ * Function: dm9x_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timesout and the DM90x0 is reset
+ * 3. During normal TX polling
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int dm9x_uiptxpoll(struct uip_driver_s *dev)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)dev->d_private;
+
+ /* If the polling resulted in data that should be sent out on the network,
+ * the field d_len is set to a value > 0.
+ */
+
+ if (dm9x->dm_dev.d_len > 0)
+ {
+ uip_arp_out(&dm9x->dm_dev);
+ dm9x_transmit(dm9x);
+
+ /* Check if there is room in the DM90x0 to hold another packet. In 100M mode,
+ * that can be 2 packets, otherwise it is a single packet.
+ */
+
+ if (dm9x->dm_ntxpending > 1 || !dm9x->dm_b100M)
+ {
+ /* Returning a non-zero value will terminate the poll operation */
+
+ return 1;
+ }
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: dm9x_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_receive(struct dm9x_driver_s *dm9x)
+{
+ union rx_desc_u rx;
+ bool bchecksumready;
+ uint8_t mdrah;
+ uint8_t mdral;
+ uint8_t rxbyte;
+
+ nvdbg("Packet received\n");
+
+ do
+ {
+ /* Store the value of memory data read address register */
+
+ mdrah = getreg(DM9X_MDRAH);
+ mdral = getreg(DM9X_MDRAL);
+
+ getreg(DM9X_MRCMDX); /* Dummy read */
+ rxbyte = (uint8_t)DM9X_DATA; /* Get the most up-to-date data */
+
+ /* Packet ready for receive check */
+
+ bchecksumready = dm9x_rxchecksumready(rxbyte);
+ if (!bchecksumready)
+ {
+ break;
+ }
+
+ /* A packet is ready now. Get status/length */
+
+ DM9X_INDEX = DM9X_MRCMD; /* set read ptr ++ */
+
+ /* Read packet status & length */
+
+ dm9x->dm_read((uint8_t*)&rx, 4);
+
+ /* Check if any errors were reported by the hardware */
+
+ if (rx.desc.rx_status & 0xbf)
+ {
+ /* Bad RX packet... update statistics */
+
+#if defined(CONFIG_DM9X_STATS)
+ if (rx.desc.rx_status & 0x01)
+ {
+ dm9x->dm_nrxfifoerrors++;
+ ndbg("RX FIFO error: %d\n", dm9x->dm_nrxfifoerrors);
+ }
+
+ if (rx.desc.rx_status & 0x02)
+ {
+ dm9x->dm_nrxcrcerrors++;
+ ndbg("RX CRC error: %d\n", dm9x->dm_nrxcrcerrors);
+ }
+
+ if (rx.desc.rx_status & 0x80)
+ {
+ dm9x->dm_nrxlengtherrors++;
+ ndbg("RX length error: %d\n", dm9x->dm_nrxlengtherrors);
+ }
+
+ if (rx.desc.rx_status & 0x08)
+ {
+ dm9x->dm_nphyserrors++;
+ ndbg("Physical Layer error: %d\n", dm9x->dm_nphyserrors);
+ }
+#else
+ ndbg("Received packet with errors: %02x\n", rx.desc.rx_status);
+#endif
+ /* Drop this packet and continue to check the next packet */
+
+ dm9x->dm_discard(rx.desc.rx_len);
+ }
+
+ /* Also check if the packet is a valid size for the uIP configuration */
+
+ else if (rx.desc.rx_len < UIP_LLH_LEN || rx.desc.rx_len > (CONFIG_NET_BUFSIZE + 2))
+ {
+#if defined(CONFIG_DM9X_STATS)
+ dm9x->dm_nrxlengtherrors++;
+ ndbg("RX length error: %d\n", dm9x->dm_nrxlengtherrors);
+#endif
+ /* Drop this packet and continue to check the next packet */
+
+ dm9x->dm_discard(rx.desc.rx_len);
+ }
+ else
+ {
+ /* Good packet... Copy the packet data out of SRAM and pass it one to uIP */
+
+ dm9x->dm_dev.d_len = rx.desc.rx_len;
+ dm9x->dm_read(dm9x->dm_dev.d_buf, rx.desc.rx_len);
+
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ uip_arp_ipin(&dm9x->dm_dev);
+ uip_input(&dm9x->dm_dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (dm9x->dm_dev.d_len > 0)
+ {
+ uip_arp_out(&dm9x->dm_dev);
+ dm9x_transmit(dm9x);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP))
+ {
+ uip_arp_arpin(&dm9x->dm_dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (dm9x->dm_dev.d_len > 0)
+ {
+ dm9x_transmit(dm9x);
+ }
+ }
+ }
+
+#if defined(CONFIG_DM9X_STATS)
+ dm9x->dm_nrxpackets++;
+ dm9x->dm_nrxbytes += rx.desc.rx_len;
+#endif
+ dm9x->ncrxpackets++;
+ }
+ while ((rxbyte & 0x01) == DM9X_PKTRDY && dm9x->ncrxpackets < DM9X_CRXTHRES);
+ nvdbg("All RX packets processed\n");
+}
+
+/****************************************************************************
+ * Function: dm9x_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_txdone(struct dm9x_driver_s *dm9x)
+{
+ int nsr;
+
+ nvdbg("TX done\n");
+
+ /* Another packet has completed transmission. Decrement the count of
+ * of pending TX transmissions.
+ */
+
+ nsr = getreg(DM9X_NETS);
+ if (nsr & DM9X_NETS_TX1END)
+ {
+ if (dm9x->dm_ntxpending)
+ {
+ dm9x->dm_ntxpending--;
+ }
+ else
+ {
+ ndbg("Bad TX count (TX1END)\n");
+ }
+ }
+
+ if (nsr & DM9X_NETS_TX2END)
+ {
+ if (dm9x->dm_ntxpending)
+ {
+ dm9x->dm_ntxpending--;
+ }
+ else
+ {
+ ndbg("Bad TX count (TX2END)\n");
+ }
+ }
+
+ /* Cancel the TX timeout */
+
+ if (dm9x->dm_ntxpending == 0)
+ {
+ wd_cancel(dm9x->dm_txtimeout);
+ }
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&dm9x->dm_dev, dm9x_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: dm9x_interrupt
+ *
+ * Description:
+ * DM90x0 interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int dm9x_interrupt(int irq, FAR void *context)
+{
+#if CONFIG_DM9X_NINTERFACES == 1
+ register struct dm9x_driver_s *dm9x = &g_dm9x[0];
+#else
+# error "Additional logic needed to support multiple interfaces"
+#endif
+ uint8_t isr;
+ uint8_t save;
+ int i;
+
+ /* Save previous register address */
+
+ save = (uint8_t)DM9X_INDEX;
+
+ /* Disable all DM90x0 interrupts */
+
+ putreg(DM9X_IMR, DM9X_IMRDISABLE);
+
+ /* Get and clear the DM90x0 interrupt status bits */
+
+ isr = getreg(DM9X_ISR);
+ putreg(DM9X_ISR, isr);
+ nvdbg("Interrupt status: %02x\n", isr);
+
+ /* Check for link status change */
+
+ if (isr & DM9X_INT_LNKCHG)
+ {
+ /* Wait up to 0.5s for link OK */
+
+ for (i = 0; i < 500; i++)
+ {
+ dm9x_phyread(dm9x,0x1);
+ if (dm9x_phyread(dm9x,0x1) & 0x4) /*Link OK*/
+ {
+ /* Wait to get detected speed */
+
+ for (i = 0; i < 200; i++)
+ {
+ up_mdelay(1);
+ }
+
+ /* Set the new network speed */
+
+ if (dm9x_phyread(dm9x, 0) & 0x2000)
+ {
+ dm9x->dm_b100M = true;
+ }
+ else
+ {
+ dm9x->dm_b100M = false;
+ }
+ break;
+ }
+ up_mdelay(1);
+ }
+ ndbg("delay: %dmS speed: %s\n", i, dm9x->dm_b100M ? "100M" : "10M");
+ }
+
+ /* Check if we received an incoming packet */
+
+ if (isr & DM9X_INT_PR)
+ {
+ dm9x_receive(dm9x);
+ }
+
+ /* Check if we are able to transmit a packet */
+
+ if (isr & DM9X_INT_PT)
+ {
+ dm9x_txdone(dm9x);
+ }
+
+ /* If the number of consecutive receive packets exceeds a threshold,
+ * then disable the RX interrupt.
+ */
+
+ if (dm9x->ncrxpackets >= DM9X_CRXTHRES)
+ {
+ /* Eanble all DM90x0 interrupts EXCEPT for RX */
+
+ putreg(DM9X_IMR, DM9X_IMRRXDISABLE);
+ }
+ else
+ {
+ /* Enable all DM90x0 interrupts */
+
+ putreg(DM9X_IMR, DM9X_IMRENABLE);
+ }
+
+ /* Restore previous register address */
+
+ DM9X_INDEX = save;
+ return OK;
+}
+
+/****************************************************************************
+ * Function: dm9x_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the DM90x0 and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_txtimeout(int argc, uint32_t arg, ...)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)arg;
+
+ ndbg("TX timeout\n");
+
+ /* Increment statistics and dump debug info */
+
+#if defined(CONFIG_DM9X_STATS)
+ dm9x->dm_ntxtimeouts++;
+ dm9x->dm_ntxerrors++;
+#endif
+
+ ndbg(" TX packet count: %d\n", dm9x->dm_ntxpending);
+#if defined(CONFIG_DM9X_STATS)
+ ndbg(" TX timeouts: %d\n", dm9x->dm_ntxtimeouts);
+#endif
+ ndbg(" TX read pointer address: 0x%02x:%02x\n",
+ getreg(DM9X_TRPAH), getreg(DM9X_TRPAL));
+ ndbg(" Memory data write address: 0x%02x:%02x (DM9010)\n",
+ getreg(DM9X_MDWAH), getreg(DM9X_MDWAL));
+
+ /* Then reset the DM90x0 */
+
+ dm9x_reset(dm9x);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&dm9x->dm_dev, dm9x_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: dm9x_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_polltimer(int argc, uint32_t arg, ...)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)arg;
+
+ /* If the number of contiguous RX packets exceeds a threshold, reset the counter and
+ * re-enable RX interrupts
+ */
+
+ if (dm9x->ncrxpackets >= DM9X_CRXTHRES)
+ {
+ dm9x->ncrxpackets = 0;
+ putreg(DM9X_IMR, DM9X_IMRENABLE);
+ }
+
+ /* Check if there is room in the DM90x0 to hold another packet. In 100M mode,
+ * that can be 2 packets, otherwise it is a single packet.
+ */
+
+ if (dm9x->dm_ntxpending < 1 || (dm9x->dm_b100M && dm9x->dm_ntxpending < 2))
+ {
+ /* If so, update TCP timing states and poll uIP for new XMIT data */
+
+ (void)uip_timer(&dm9x->dm_dev, dm9x_uiptxpoll, DM6X_POLLHSEC);
+ }
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(dm9x->dm_txpoll, DM6X_WDDELAY, dm9x_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: dm9x_phymode
+ *
+ * Description:
+ * Configure the PHY operating mode
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void dm9x_phymode(struct dm9x_driver_s *dm9x)
+{
+ uint16_t phyreg0;
+ uint16_t phyreg4;
+
+#if CONFIG_DM9X_MODE == DM9X_MODE_AUTO
+ phyreg0 = 0x1200; /* Auto-negotiation & Restart Auto-negotiation */
+ phyreg4 = 0x01e1; /* Default flow control disable*/
+#elif CONFIG_DM9X_MODE == DM9X_MODE_10MHD
+ phyreg4 = 0x21;
+ phyreg0 = 0x1000;
+#elif CONFIG_DM9X_MODE == DM9X_MODE_10MFD
+ phyreg4 = 0x41;
+ phyreg0 = 0x1100;
+#elif CONFIG_DM9X_MODE == DM9X_MODE_100MHD
+ phyreg4 = 0x81;
+ phyreg0 = 0x3000;
+#elif CONFIG_DM9X_MODE == DM9X_MODE_100MFD
+ phyreg4 = 0x101;
+ phyreg0 = 0x3100;
+#else
+# error "Recognized PHY mode"
+#endif
+
+ dm9x_phywrite(dm9x, 0, phyreg0);
+ dm9x_phywrite(dm9x, 4, phyreg4);
+}
+
+/****************************************************************************
+ * Function: dm9x_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the DM90x0 interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int dm9x_ifup(struct uip_driver_s *dev)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)dev->d_private;
+ uint8_t netstatus;
+ int i;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initilize DM90x0 chip */
+
+ dm9x_bringup(dm9x);
+
+ /* Check link state and media speed (waiting up to 3s for link OK) */
+
+ dm9x->dm_b100M = false;
+ for (i = 0; i < 3000; i++)
+ {
+ netstatus = getreg(DM9X_NETS);
+ if (netstatus & DM9X_NETS_LINKST)
+ {
+ /* Link OK... Wait a bit before getting the detected speed */
+
+ up_mdelay(200);
+ netstatus = getreg(DM9X_NETS);
+ if ((netstatus & DM9X_NETS_SPEED) == 0)
+ {
+ dm9x->dm_b100M = true;
+ }
+ break;
+ }
+ i++;
+ up_mdelay(1);
+ }
+
+ ndbg("delay: %dmS speed: %s\n", i, dm9x->dm_b100M ? "100M" : "10M");
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(dm9x->dm_txpoll, DM6X_WDDELAY, dm9x_polltimer, 1, (uint32_t)dm9x);
+
+ /* Enable the DM9X interrupt */
+
+ dm9x->dm_bifup = true;
+ up_enable_irq(CONFIG_DM9X_IRQ);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: dm9x_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int dm9x_ifdown(struct uip_driver_s *dev)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ ndbg("Stopping\n");
+
+ /* Disable the DM9X interrupt */
+
+ flags = irqsave();
+ up_disable_irq(CONFIG_DM9X_IRQ);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(dm9x->dm_txpoll);
+ wd_cancel(dm9x->dm_txtimeout);
+
+ /* Reset the device */
+
+ dm9x_phywrite(dm9x, 0x00, 0x8000); /* PHY reset */
+ putreg(DM9X_GPD, 0x01); /* Power-down PHY (GEPIO0=1) */
+ putreg(DM9X_IMR, DM9X_IMRDISABLE); /* Disable all interrupts */
+ putreg(DM9X_RXC, 0x00); /* Disable RX */
+ putreg(DM9X_ISR, DM9X_INT_ALL); /* Clear interrupt status */
+
+ dm9x->dm_bifup = false;
+ irqrestore(flags);
+
+ /* Dump statistics */
+
+ dm9x_dumpstatistics(dm9x);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: dm9x_txavail
+ *
+ * Description:
+ * Driver callback invoked when new TX data is available. This is a
+ * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ * latency.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int dm9x_txavail(struct uip_driver_s *dev)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ ndbg("Polling\n");
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (dm9x->dm_bifup)
+ {
+
+ /* Check if there is room in the DM90x0 to hold another packet. In 100M
+ * mode, that can be 2 packets, otherwise it is a single packet.
+ */
+
+ if (dm9x->dm_ntxpending < 1 || (dm9x->dm_b100M && dm9x->dm_ntxpending < 2))
+ {
+ /* If so, then poll uIP for new XMIT data */
+
+ (void)uip_poll(&dm9x->dm_dev, dm9x_uiptxpoll);
+ }
+ }
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: dm9x_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int dm9x_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct dm9x_driver_s *priv = (FAR struct dm9x_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int dm9x_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct dm9x_driver_s *priv = (FAR struct dm9x_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_bringup
+ *
+ * Description:
+ * Initialize the dm90x0 chip
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_bringup(struct dm9x_driver_s *dm9x)
+{
+ ndbg("Initializing\n");
+
+ /* Set the internal PHY power-on, GPIOs normal, and wait 2ms */
+
+ putreg(DM9X_GPD, 0x01); /* Power-down the PHY (GEPIO0=1) */
+ up_udelay(500);
+ putreg(DM9X_GPD, 0x00); /* Preactivate PHY (GPIO0=0 */
+ up_udelay(20); /* Wait 20us for PHY power-on ready */
+
+ /* Do a software reset and wait 20us (twice). The reset autoclears
+ * in 10us; 20us guarantees completion of the reset
+ */
+
+ putreg(DM9X_NETC, (DM9X_NETC_RST|DM9X_NETC_LBK1));
+ up_udelay(20);
+ putreg(DM9X_NETC, (DM9X_NETC_RST|DM9X_NETC_LBK1));
+ up_udelay(20);
+
+ /* Configure I/O mode */
+
+ switch (getreg(DM9X_ISR) & DM9X_ISR_IOMODEM)
+ {
+ case DM9X_ISR_IOMODE8:
+ dm9x->dm_read = read8;
+ dm9x->dm_write = write8;
+ dm9x->dm_discard = discard8;
+ break;
+
+ case DM9X_ISR_IOMODE16:
+ dm9x->dm_read = read16;
+ dm9x->dm_write = write16;
+ dm9x->dm_discard = discard16;
+ break;
+
+ case DM9X_ISR_IOMODE32:
+ dm9x->dm_read = read32;
+ dm9x->dm_write = write32;
+ dm9x->dm_discard = discard32;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Program PHY operating mode */
+
+ dm9x_phymode(dm9x);
+
+ /* Program operating mode */
+
+ putreg(DM9X_NETC, 0x00); /* Network control */
+ putreg(DM9X_TXC, 0x00); /* Clear TX Polling */
+ putreg(DM9X_BPTHRES, 0x3f); /* Less 3kb, 600us */
+ putreg(DM9X_SMODEC, 0x00); /* Special mode */
+ putreg(DM9X_NETS, (DM9X_NETS_WAKEST|DM9X_NETS_TX1END|DM9X_NETS_TX2END)); /* Clear TX status */
+ putreg(DM9X_ISR, DM9X_INT_ALL); /* Clear interrupt status */
+
+#if defined(CONFIG_DM9X_CHECKSUM)
+ putreg(DM9X_TCCR, 0x07); /* TX UDP/TCP/IP checksum enable */
+ putreg(DM9X_RCSR, 0x02); /* Receive checksum enable */
+#endif
+
+#if defined(CONFIG_DM9X_ETRANS)
+ putreg(DM9X_ETXCSR, 0x83);
+#endif
+
+ /* Initialize statistics */
+
+ dm9x->ncrxpackets = 0; /* Number of continuous RX packets */
+ dm9x->dm_ntxpending = 0; /* Number of pending TX packets */
+ dm9x_resetstatistics(dm9x);
+
+ /* Activate DM9000A/DM9010 */
+
+ putreg(DM9X_RXC, DM9X_RXCSETUP | 1); /* RX enable */
+ putreg(DM9X_IMR, DM9X_IMRENABLE); /* Enable TX/RX interrupts */
+}
+
+/****************************************************************************
+ * Function: dm9x_reset
+ *
+ * Description:
+ * Stop, reset, re-initialize, and restart the DM90x0 chip and driver. At
+ * present, the chip is only reset after a TX timeout.
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_reset(struct dm9x_driver_s *dm9x)
+{
+ uint8_t save;
+ int i;
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(dm9x->dm_txpoll);
+ wd_cancel(dm9x->dm_txtimeout);
+
+ /* Save previous register address */
+
+ save = (uint8_t)DM9X_INDEX;
+
+#if defined(CONFIG_DM9X_STATS)
+ dm9x->dm_nresets++;
+#endif
+ dm9x_bringup(dm9x);
+
+ /* Wait up to 1 second for the link to be OK */
+
+ dm9x->dm_b100M = false;
+ for (i = 0; i < 1000; i++)
+ {
+ if (dm9x_phyread(dm9x,0x1) & 0x4)
+ {
+ if (dm9x_phyread(dm9x, 0) &0x2000)
+ {
+ dm9x->dm_b100M = true;
+ }
+ break;
+ }
+ up_mdelay(1);
+ }
+
+ /* Restore previous register address */
+
+ DM9X_INDEX = save;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: dm9x_initialize
+ *
+ * Description:
+ * Initialize the DM90x0 driver
+ *
+ * Parameters:
+ * None
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+/* Initialize the DM90x0 chip and driver */
+
+int dm9x_initialize(void)
+{
+ uint8_t *mptr;
+ uint16_t vid;
+ uint16_t pid;
+ int i;
+ int j;
+
+ /* Get the chip vendor ID and product ID */
+
+ vid = (((uint16_t)getreg(DM9X_VIDH)) << 8) | (uint16_t)getreg(DM9X_VIDL);
+ pid = (((uint16_t)getreg(DM9X_PIDH)) << 8) | (uint16_t)getreg(DM9X_PIDL);
+ nlldbg("I/O base: %08x VID: %04x PID: %04x\n", CONFIG_DM9X_BASE, vid, pid);
+
+ /* Check if a DM90x0 chip is recognized at this I/O base */
+
+ if (vid != DM9X_DAVICOMVID || (pid != DM9X_DM9000PID && pid != DM9X_DM9010PID))
+ {
+ nlldbg("DM90x0 vendor/product ID not found at this base address\n");
+ return -ENODEV;
+ }
+
+ /* Attach the IRQ to the driver */
+
+ if (irq_attach(CONFIG_DM9X_IRQ, dm9x_interrupt))
+ {
+ /* We could not attach the ISR to the ISR */
+
+ nlldbg("irq_attach() failed\n");
+ return -EAGAIN;
+ }
+
+ /* Initialize the driver structure */
+
+ memset(g_dm9x, 0, CONFIG_DM9X_NINTERFACES*sizeof(struct dm9x_driver_s));
+ g_dm9x[0].dm_dev.d_ifup = dm9x_ifup; /* I/F down callback */
+ g_dm9x[0].dm_dev.d_ifdown = dm9x_ifdown; /* I/F up (new IP address) callback */
+ g_dm9x[0].dm_dev.d_txavail = dm9x_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ g_dm9x[0].dm_dev.d_addmac = dm9x_addmac; /* Add multicast MAC address */
+ g_dm9x[0].dm_dev.d_rmmac = dm9x_rmmac; /* Remove multicast MAC address */
+#endif
+ g_dm9x[0].dm_dev.d_private = (void*)g_dm9x; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ g_dm9x[0].dm_txpoll = wd_create(); /* Create periodic poll timer */
+ g_dm9x[0].dm_txtimeout = wd_create(); /* Create TX timeout timer */
+
+ /* Read the MAC address */
+
+ mptr = g_dm9x[0].dm_dev.d_mac.ether_addr_octet;
+ for (i = 0, j = DM9X_PAB0; i < ETHER_ADDR_LEN; i++, j++)
+ {
+ mptr[i] = getreg(j);
+ }
+
+ nlldbg("MAC: %0x:%0x:%0x:%0x:%0x:%0x\n",
+ mptr[0], mptr[1], mptr[2], mptr[3], mptr[4], mptr[5]);
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ (void)netdev_register(&g_dm9x[0].dm_dev);
+ return OK;
+}
+
+#endif /* CONFIG_NET && CONFIG_NET_DM90x0 */
+
diff --git a/nuttx/drivers/net/e1000.c b/nuttx/drivers/net/e1000.c
new file mode 100644
index 000000000..ec2b29b6a
--- /dev/null
+++ b/nuttx/drivers/net/e1000.c
@@ -0,0 +1,1049 @@
+/****************************************************************************
+ * drivers/net/e1000.c
+ *
+ * Copyright (C) 2011 Yu Qiang. All rights reserved.
+ * Author: Yu Qiang <yuq825@gmail.com>
+ *
+ * This file is a part of NuttX:
+ *
+ * Copyright (C) 2011 Gregory Nutt. 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/kmalloc.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#include <rgmp/pmap.h>
+#include <rgmp/string.h>
+#include <rgmp/stdio.h>
+#include <rgmp/arch/pci.h>
+#include <rgmp/memio.h>
+#include "e1000.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define E1000_WDDELAY (1*CLK_TCK)
+#define E1000_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define E1000_TXTIMEOUT (60*CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)e1000->uip_dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct tx_ring {
+ struct tx_desc *desc;
+ char *buf;
+ int tail; // where to write desc
+};
+
+struct rx_ring {
+ struct rx_desc *desc;
+ char *buf;
+ int head; // where to read
+ int tail; // where to release free desc
+ int free; // number of freed desc
+};
+
+struct e1000_dev {
+ uint32_t phy_mem_base;
+ uint32_t io_mem_base;
+ uint32_t mem_size;
+ int pci_dev_id;
+ unsigned char src_mac[6];
+ unsigned char dst_mac[6];
+ int irq;
+ struct irq_action int_desc;
+ struct tx_ring tx_ring;
+ struct rx_ring rx_ring;
+ struct e1000_dev *next;
+
+ // NuttX net data
+ bool bifup; /* true:ifup false:ifdown */
+ WDOG_ID txpoll; /* TX poll timer */
+ WDOG_ID txtimeout; /* TX timeout timer */
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s uip_dev; /* Interface understood by uIP */
+};
+
+struct e1000_dev_head {
+ struct e1000_dev *next;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct e1000_dev_head e1000_list = {0};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static int e1000_transmit(struct e1000_dev *e1000);
+static int e1000_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void e1000_receive(struct e1000_dev *e1000);
+
+/* Watchdog timer expirations */
+
+static void e1000_polltimer(int argc, uint32_t arg, ...);
+static void e1000_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int e1000_ifup(struct uip_driver_s *dev);
+static int e1000_ifdown(struct uip_driver_s *dev);
+static int e1000_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int e1000_addmac(struct uip_driver_s *dev, const uint8_t *mac);
+static int e1000_rmmac(struct uip_driver_s *dev, const uint8_t *mac);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline void e1000_outl(struct e1000_dev *dev, int reg, uint32_t val)
+{
+ writel(dev->io_mem_base+reg, val);
+}
+
+static inline uint32_t e1000_inl(struct e1000_dev *dev, int reg)
+{
+ return readl(dev->io_mem_base+reg);
+}
+
+/****************************** e1000 driver ********************************/
+
+void e1000_reset(struct e1000_dev *dev)
+{
+ uint32_t dev_control;
+
+ // Reset the network controller hardware
+ dev_control = 0;
+ dev_control |= (1<<0); // FD-bit (Full Duplex)
+ dev_control |= (0<<2); // GIOMD-bit (GIO Master Disable)
+ dev_control |= (1<<3); // LRST-bit (Link Reset)
+ dev_control |= (1<<6); // SLU-bit (Set Link Up)
+ dev_control |= (2<<8); // SPEED=2 (1000Mbps)
+ dev_control |= (0<<11); // FRCSPD-bit (Force Speed)
+ dev_control |= (0<<12); // FRCDPLX-bit (Force Duplex)
+ dev_control |= (0<<20); // ADVD3WUC-bit (Advertise D3 Wake Up Cap)
+ dev_control |= (1<<26); // RST-bit (Device Reset)
+ dev_control |= (1<<27); // RFCE-bit (Receive Flow Control Enable)
+ dev_control |= (1<<28); // TFCE-bit (Transmit Flow Control Enable)
+ dev_control |= (0<<30); // VME-bit (VLAN Mode Enable)
+ dev_control |= (0<<31); // PHY_RST-bit (PHY Reset)
+
+ e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
+ e1000_outl(dev, E1000_STATUS, 0x00000000);
+ e1000_outl(dev, E1000_CTRL, dev_control);
+ dev_control &= ~(1<<26); // clear RST-bit (Device Reset)
+ e1000_outl(dev, E1000_CTRL, dev_control);
+ up_mdelay(10);
+ e1000_outl(dev, E1000_CTRL_EXT, 0x001401C0);
+ e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
+}
+
+void e1000_turn_on(struct e1000_dev *dev)
+{
+ int tx_control, rx_control;
+ uint32_t ims = 0;
+
+ // turn on the controller's receive engine
+ rx_control = e1000_inl(dev, E1000_RCTL);
+ rx_control |= (1<<1);
+ e1000_outl(dev, E1000_RCTL, rx_control);
+
+ // turn on the controller's transmit engine
+ tx_control = e1000_inl(dev, E1000_TCTL);
+ tx_control |= (1<<1);
+ e1000_outl(dev, E1000_TCTL, tx_control);
+
+ // enable the controller's interrupts
+ e1000_outl(dev, E1000_ICR, 0xFFFFFFFF);
+ e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
+
+ ims |= 1<<0; // TXDW
+ ims |= 1<<1; // TXQE
+ ims |= 1<<2; // LSC
+ ims |= 1<<4; // RXDMT0
+ ims |= 1<<7; // RXT0
+ e1000_outl(dev, E1000_IMS, ims);
+}
+
+void e1000_turn_off(struct e1000_dev *dev)
+{
+ int tx_control, rx_control;
+
+ // turn off the controller's receive engine
+ rx_control = e1000_inl(dev, E1000_RCTL);
+ rx_control &= ~(1<<1);
+ e1000_outl(dev, E1000_RCTL, rx_control);
+
+ // turn off the controller's transmit engine
+ tx_control = e1000_inl(dev, E1000_TCTL);
+ tx_control &= ~(1<<1);
+ e1000_outl(dev, E1000_TCTL, tx_control);
+
+ // turn off the controller's interrupts
+ e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
+}
+
+void e1000_init(struct e1000_dev *dev)
+{
+ uint32_t rxd_phys, txd_phys, kmem_phys;
+ uint32_t rx_control, tx_control;
+ uint32_t pba;
+ int i;
+
+ e1000_reset(dev);
+
+ // configure the controller's 'receive' engine
+ rx_control = 0;
+ rx_control |= (0<<1); // EN-bit (Enable)
+ rx_control |= (0<<2); // SPB-bit (Store Bad Packets)
+ rx_control |= (0<<3); // UPE-bit (Unicast Promiscuous Mode)
+ rx_control |= (1<<4); // MPE-bit (Multicast Promiscuous Mode)
+ rx_control |= (0<<5); // LPE-bit (Long Packet Enable)
+ rx_control |= (0<<6); // LBM=0 (Loop-Back Mode)
+ rx_control |= (0<<8); // RDMTS=0 (Rx Descriptor Min Threshold Size)
+ rx_control |= (0<<10); // DTYPE=0 (Descriptor Type)
+ rx_control |= (0<<12); // MO=0 (Multicast Offset)
+ rx_control |= (1<<15); // BAM-bit (Broadcast Address Mode)
+ rx_control |= (0<<16); // BSIZE=0 (Buffer Size = 2048)
+ rx_control |= (0<<18); // VLE-bit (VLAN filter Enable)
+ rx_control |= (0<<19); // CFIEN-bit (Canonical Form Indicator Enable)
+ rx_control |= (0<<20); // CFI-bit (Canonical Form Indicator)
+ rx_control |= (1<<22); // DPF-bit (Discard Pause Frames)
+ rx_control |= (0<<23); // PMCF-bit (Pass MAC Control Frames)
+ rx_control |= (0<<25); // BSEX=0 (Buffer Size EXtension)
+ rx_control |= (1<<26); // SECRC-bit (Strip Ethernet CRC)
+ rx_control |= (0<<27); // FLEXBUF=0 (Flexible Buffer size)
+ e1000_outl(dev, E1000_RCTL, rx_control);
+
+ // configure the controller's 'transmit' engine
+ tx_control = 0;
+ tx_control |= (0<<1); // EN-bit (Enable)
+ tx_control |= (1<<3); // PSP-bit (Pad Short Packets)
+ tx_control |= (15<<4); // CT=15 (Collision Threshold)
+ tx_control |= (63<<12); // COLD=63 (Collision Distance)
+ tx_control |= (0<<22); // SWXOFF-bit (Software XOFF)
+ tx_control |= (1<<24); // RTLC-bit (Re-Transmit on Late Collision)
+ tx_control |= (0<<25); // UNORTX-bit (Underrun No Re-Transmit)
+ tx_control |= (0<<26); // TXCSCMT=0 (TxDesc Mininum Threshold)
+ tx_control |= (0<<28); // MULR-bit (Multiple Request Support)
+ e1000_outl(dev, E1000_TCTL, tx_control);
+
+ // hardware flow control
+ pba = e1000_inl(dev, E1000_PBA);
+ // get receive FIFO size
+ pba = (pba & 0x000000ff)<<10;
+ e1000_outl(dev, E1000_FCAL, 0x00C28001);
+ e1000_outl(dev, E1000_FCAH, 0x00000100);
+ e1000_outl(dev, E1000_FCT, 0x00008808);
+ e1000_outl(dev, E1000_FCTTV, 0x00000680);
+ e1000_outl(dev, E1000_FCRTL, (pba*8/10)|0x80000000);
+ e1000_outl(dev, E1000_FCRTH, pba*9/10);
+
+ // setup tx rings
+ txd_phys = PADDR(dev->tx_ring.desc);
+ kmem_phys = PADDR(dev->tx_ring.buf);
+ for (i=0; i<CONFIG_E1000_N_TX_DESC; i++,kmem_phys+=CONFIG_E1000_BUFF_SIZE) {
+ dev->tx_ring.desc[i].base_address = kmem_phys;
+ dev->tx_ring.desc[i].packet_length = 0;
+ dev->tx_ring.desc[i].cksum_offset = 0;
+ dev->tx_ring.desc[i].cksum_origin = 0;
+ dev->tx_ring.desc[i].desc_status = 1;
+ dev->tx_ring.desc[i].desc_command = (1<<0)|(1<<1)|(1<<3);
+ dev->tx_ring.desc[i].special_info = 0;
+ }
+ dev->tx_ring.tail = 0;
+ e1000_outl(dev, E1000_TDT, 0);
+ e1000_outl(dev, E1000_TDH, 0);
+ // tell controller the location, size, and fetch-policy for Tx queue
+ e1000_outl(dev, E1000_TDBAL, txd_phys);
+ e1000_outl(dev, E1000_TDBAH, 0x00000000);
+ e1000_outl(dev, E1000_TDLEN, CONFIG_E1000_N_TX_DESC*16);
+ e1000_outl(dev, E1000_TXDCTL, 0x01010000);
+
+ // setup rx rings
+ rxd_phys = PADDR(dev->rx_ring.desc);
+ kmem_phys = PADDR(dev->rx_ring.buf);
+ for (i=0; i<CONFIG_E1000_N_RX_DESC; i++,kmem_phys+=CONFIG_E1000_BUFF_SIZE) {
+ dev->rx_ring.desc[i].base_address = kmem_phys;
+ dev->rx_ring.desc[i].packet_length = 0;
+ dev->rx_ring.desc[i].packet_cksum = 0;
+ dev->rx_ring.desc[i].desc_status = 0;
+ dev->rx_ring.desc[i].desc_errors = 0;
+ dev->rx_ring.desc[i].vlan_tag = 0;
+ }
+ dev->rx_ring.head = 0;
+ dev->rx_ring.tail = CONFIG_E1000_N_RX_DESC-1;
+ dev->rx_ring.free = 0;
+ // give the controller ownership of all receive descriptors
+ e1000_outl(dev, E1000_RDH, 0);
+ e1000_outl(dev, E1000_RDT, CONFIG_E1000_N_RX_DESC-1);
+ // tell controller the location, size, and fetch-policy for RX queue
+ e1000_outl(dev, E1000_RDBAL, rxd_phys);
+ e1000_outl(dev, E1000_RDBAH, 0x00000000);
+ e1000_outl(dev, E1000_RDLEN, CONFIG_E1000_N_RX_DESC*16);
+ e1000_outl(dev, E1000_RXDCTL, 0x01010000);
+
+ e1000_turn_on(dev);
+}
+
+/****************************************************************************
+ * Function: e1000_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * e1000 - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int e1000_transmit(struct e1000_dev *e1000)
+{
+ int tail = e1000->tx_ring.tail;
+ unsigned char *cp = (unsigned char *)
+ (e1000->tx_ring.buf + tail * CONFIG_E1000_BUFF_SIZE);
+ int count = e1000->uip_dev.d_len;
+
+ /* Verify that the hardware is ready to send another packet. If we get
+ * here, then we are committed to sending a packet; Higher level logic
+ * must have assured that there is not transmission in progress.
+ */
+
+ if (!e1000->tx_ring.desc[tail].desc_status)
+ return -1;
+
+ /* Increment statistics */
+
+ /* Send the packet: address=skel->sk_dev.d_buf, length=skel->sk_dev.d_len */
+ memcpy(cp, e1000->uip_dev.d_buf, e1000->uip_dev.d_len);
+
+ // prepare the transmit-descriptor
+ e1000->tx_ring.desc[tail].packet_length = count<60 ? 60:count;
+ e1000->tx_ring.desc[tail].desc_status = 0;
+
+ // give ownership of this descriptor to the network controller
+ tail = (tail + 1) % CONFIG_E1000_N_TX_DESC;
+ e1000->tx_ring.tail = tail;
+ e1000_outl(e1000, E1000_TDT, tail);
+
+ /* Enable Tx interrupts */
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ wd_start(e1000->txtimeout, E1000_TXTIMEOUT, e1000_txtimeout, 1, (uint32_t)e1000);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: e1000_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timesout and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int e1000_uiptxpoll(struct uip_driver_s *dev)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+ int tail = e1000->tx_ring.tail;
+
+ /* If the polling resulted in data that should be sent out on the network,
+ * the field d_len is set to a value > 0.
+ */
+
+ if (e1000->uip_dev.d_len > 0) {
+ uip_arp_out(&e1000->uip_dev);
+ e1000_transmit(e1000);
+
+ /* Check if there is room in the device to hold another packet. If not,
+ * return a non-zero value to terminate the poll.
+ */
+ if (!e1000->tx_ring.desc[tail].desc_status)
+ return -1;
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: e1000_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * e1000 - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void e1000_receive(struct e1000_dev *e1000)
+{
+ int head = e1000->rx_ring.head;
+ unsigned char *cp = (unsigned char *)
+ (e1000->rx_ring.buf + head * CONFIG_E1000_BUFF_SIZE);
+ int cnt;
+
+ while (e1000->rx_ring.desc[head].desc_status) {
+
+ /* Check for errors and update statistics */
+
+ // Here we do not handle packets that exceed packet-buffer size
+ if ((e1000->rx_ring.desc[head].desc_status & 3) == 1) {
+ cprintf("NIC READ: Oversized packet\n");
+ goto next;
+ }
+
+ /* Check if the packet is a valid size for the uIP buffer configuration */
+
+ // get the number of actual data-bytes in this packet
+ cnt = e1000->rx_ring.desc[head].packet_length;
+
+ if (cnt > CONFIG_NET_BUFSIZE || cnt < 14) {
+ cprintf("NIC READ: invalid package size\n");
+ goto next;
+ }
+
+ /* Copy the data data from the hardware to e1000->uip_dev.d_buf. Set
+ * amount of data in e1000->uip_dev.d_len
+ */
+
+ // now we try to copy these data-bytes to the UIP buffer
+ memcpy(e1000->uip_dev.d_buf, cp, cnt);
+ e1000->uip_dev.d_len = cnt;
+
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ uip_arp_ipin(&e1000->uip_dev);
+ uip_input(&e1000->uip_dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (e1000->uip_dev.d_len > 0) {
+ uip_arp_out(&e1000->uip_dev);
+ e1000_transmit(e1000);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP)) {
+ uip_arp_arpin(&e1000->uip_dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (e1000->uip_dev.d_len > 0) {
+ e1000_transmit(e1000);
+ }
+ }
+
+ next:
+ e1000->rx_ring.desc[head].desc_status = 0;
+ e1000->rx_ring.head = (head + 1) % CONFIG_E1000_N_RX_DESC;
+ e1000->rx_ring.free++;
+ head = e1000->rx_ring.head;
+ cp = (unsigned char *)(e1000->rx_ring.buf + head * CONFIG_E1000_BUFF_SIZE);
+ }
+}
+
+/****************************************************************************
+ * Function: e1000_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void e1000_txtimeout(int argc, uint32_t arg, ...)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)arg;
+
+ /* Increment statistics and dump debug info */
+
+ /* Then reset the hardware */
+ e1000_init(e1000);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&e1000->uip_dev, e1000_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: e1000_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void e1000_polltimer(int argc, uint32_t arg, ...)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)arg;
+ int tail = e1000->tx_ring.tail;
+
+ /* Check if there is room in the send another TX packet. We cannot perform
+ * the TX poll if he are unable to accept another packet for transmission.
+ */
+ if (!e1000->tx_ring.desc[tail].desc_status)
+ return;
+
+ /* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm..
+ * might be bug here. Does this mean if there is a transmit in progress,
+ * we will missing TCP time state updates?
+ */
+
+ (void)uip_timer(&e1000->uip_dev, e1000_uiptxpoll, E1000_POLLHSEC);
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(e1000->txpoll, E1000_WDDELAY, e1000_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: e1000_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int e1000_ifup(struct uip_driver_s *dev)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */
+ e1000_init(e1000);
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(e1000->txpoll, E1000_WDDELAY, e1000_polltimer, 1, (uint32_t)e1000);
+
+ if (e1000_inl(e1000, E1000_STATUS) & 2)
+ e1000->bifup = true;
+ else
+ e1000->bifup = false;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: e1000_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int e1000_ifdown(struct uip_driver_s *dev)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+
+ e1000_turn_off(e1000);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(e1000->txpoll);
+ wd_cancel(e1000->txtimeout);
+
+ /* Put the the EMAC is its reset, non-operational state. This should be
+ * a known configuration that will guarantee the skel_ifup() always
+ * successfully brings the interface back up.
+ */
+ //e1000_reset(e1000);
+
+ /* Mark the device "down" */
+
+ e1000->bifup = false;
+ irqrestore(flags);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: e1000_txavail
+ *
+ * Description:
+ * Driver callback invoked when new TX data is available. This is a
+ * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ * latency.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int e1000_txavail(struct uip_driver_s *dev)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+ int tail = e1000->tx_ring.tail;
+ irqstate_t flags;
+
+ /* Disable interrupts because this function may be called from interrupt
+ * level processing.
+ */
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (e1000->bifup) {
+ /* Check if there is room in the hardware to hold another outgoing packet. */
+ if (e1000->tx_ring.desc[tail].desc_status)
+ (void)uip_poll(&e1000->uip_dev, e1000_uiptxpoll);
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: e1000_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int e1000_addmac(struct uip_driver_s *dev, const uint8_t *mac)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: e1000_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int e1000_rmmac(struct uip_driver_s *dev, const uint8_t *mac)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+irqreturn_t e1000_interrupt_handler(struct Trapframe *tf, void *dev_id)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev_id;
+
+ /* Get and clear interrupt status bits */
+ int intr_cause = e1000_inl(e1000, E1000_ICR);
+ e1000_outl(e1000, E1000_ICR, intr_cause);
+
+ // not for me
+ if (intr_cause == 0)
+ return IRQ_NONE;
+
+ /* Handle interrupts according to status bit settings */
+
+ // Link status change
+ if (intr_cause & (1<<2)) {
+ if (e1000_inl(e1000, E1000_STATUS) & 2)
+ e1000->bifup = true;
+ else
+ e1000->bifup = false;
+ }
+
+ /* Check if we received an incoming packet, if so, call skel_receive() */
+
+ // Rx-descriptor Timer expired
+ if (intr_cause & (1<<7))
+ e1000_receive(e1000);
+
+ // Tx queue empty
+ if (intr_cause & (1<<1))
+ wd_cancel(e1000->txtimeout);
+
+ /* Check is a packet transmission just completed. If so, call skel_txdone.
+ * This may disable further Tx interrupts if there are no pending
+ * tansmissions.
+ */
+
+ // Tx-descriptor Written back
+ if (intr_cause & (1<<0))
+ uip_poll(&e1000->uip_dev, e1000_uiptxpoll);
+
+
+ // Rx-Descriptors Low
+ if (intr_cause & (1<<4)) {
+ int tail;
+ tail = e1000->rx_ring.tail + e1000->rx_ring.free;
+ tail %= CONFIG_E1000_N_RX_DESC;
+ e1000->rx_ring.tail = tail;
+ e1000->rx_ring.free = 0;
+ e1000_outl(e1000, E1000_RDT, tail);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/******************************* PCI driver *********************************/
+
+static pci_id_t e1000_id_table[] = {
+ {.sep = {INTEL_VENDERID, E1000_82573L}},
+ {.sep = {INTEL_VENDERID, E1000_82540EM}},
+ {.sep = {INTEL_VENDERID, E1000_82574L}},
+ {.sep = {INTEL_VENDERID, E1000_82567LM}},
+ {.sep = {INTEL_VENDERID, E1000_82541PI}},
+ {.sep = {0,0}}
+};
+
+static int e1000_probe(uint16_t addr, pci_id_t id)
+{
+ uint32_t mmio_base, mmio_size;
+ uint32_t pci_cmd, size;
+ int err, irq, flags;
+ void *kmem, *omem;
+ struct e1000_dev *dev;
+
+ // alloc e1000_dev memory
+ dev = kzalloc(sizeof(struct e1000_dev));
+ if (dev == NULL)
+ return -1;
+
+ // enable device
+ err = pci_enable_device(addr, PCI_RESOURCE_MEM);
+ if (err)
+ goto error;
+
+ // get e1000 device type
+ dev->pci_dev_id = id.join;
+
+ // remap the controller's i/o-memory into kernel's address-space
+ mmio_base = pci_resource_start(addr, 0);
+ mmio_size = pci_resource_len(addr, 0);
+ err = rgmp_memmap_nocache(mmio_base, mmio_size, mmio_base);
+ if (err)
+ goto error;
+ dev->phy_mem_base = mmio_base;
+ dev->io_mem_base = mmio_base;
+ dev->mem_size = mmio_size;
+
+ // make sure the controller's Bus Master capability is enabled
+ pci_cmd = pci_config_readl(addr, PCI_COMMAND);
+ pci_cmd |= (1<<2);
+ pci_config_writel(addr, PCI_COMMAND, pci_cmd);
+
+ // MAC address
+ memset(dev->dst_mac, 0xFF, 6);
+ memcpy(dev->src_mac, (void *)(dev->io_mem_base+E1000_RA), 6);
+
+ // get e1000 IRQ
+ flags = 0;
+ irq = pci_enable_msi(addr);
+ if (irq == 0) {
+ irq = pci_read_irq(addr);
+ flags |= IDC_SHARE;
+ }
+ dev->irq = irq;
+ dev->int_desc.handler = e1000_interrupt_handler;
+ dev->int_desc.dev_id = dev;
+ err = rgmp_request_irq(irq, &dev->int_desc, flags);
+ if (err)
+ goto err0;
+
+ // Here we alloc a big block of memory once and make it
+ // aligned to page boundary and multiple of page size. This
+ // is because the memory can be modified by E1000 DMA and
+ // should be mapped no-cache which will hugely reduce memory
+ // access performance. The page size alloc will restrict
+ // this bad effect only within the memory we alloc here.
+ size = CONFIG_E1000_N_TX_DESC * sizeof(struct tx_desc) +
+ CONFIG_E1000_N_TX_DESC * CONFIG_E1000_BUFF_SIZE +
+ CONFIG_E1000_N_RX_DESC * sizeof(struct rx_desc) +
+ CONFIG_E1000_N_RX_DESC * CONFIG_E1000_BUFF_SIZE;
+ size = ROUNDUP(size, PGSIZE);
+ omem = kmem = memalign(PGSIZE, size);
+ if (kmem == NULL) {
+ err = -ENOMEM;
+ goto err1;
+ }
+ rgmp_memremap_nocache((uintptr_t)kmem, size);
+
+ // alloc memory for tx ring
+ dev->tx_ring.desc = (struct tx_desc*)kmem;
+ kmem += CONFIG_E1000_N_TX_DESC * sizeof(struct tx_desc);
+ dev->tx_ring.buf = kmem;
+ kmem += CONFIG_E1000_N_TX_DESC * CONFIG_E1000_BUFF_SIZE;
+
+ // alloc memory for rx rings
+ dev->rx_ring.desc = (struct rx_desc*)kmem;
+ kmem += CONFIG_E1000_N_RX_DESC * sizeof(struct rx_desc);
+ dev->rx_ring.buf = kmem;
+
+ /* Initialize the driver structure */
+
+ dev->uip_dev.d_ifup = e1000_ifup; /* I/F up (new IP address) callback */
+ dev->uip_dev.d_ifdown = e1000_ifdown; /* I/F down callback */
+ dev->uip_dev.d_txavail = e1000_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ dev->uip_dev.d_addmac = e1000_addmac; /* Add multicast MAC address */
+ dev->uip_dev.d_rmmac = e1000_rmmac; /* Remove multicast MAC address */
+#endif
+ dev->uip_dev.d_private = dev; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ dev->txpoll = wd_create(); /* Create periodic poll timer */
+ dev->txtimeout = wd_create(); /* Create TX timeout timer */
+
+ // Put the interface in the down state.
+ // e1000 reset
+ e1000_reset(dev);
+
+ /* Read the MAC address from the hardware */
+ memcpy(dev->uip_dev.d_mac.ether_addr_octet, (void *)(dev->io_mem_base+E1000_RA), 6);
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+ err = netdev_register(&dev->uip_dev);
+ if (err)
+ goto err2;
+
+ // insert into e1000_list
+ dev->next = e1000_list.next;
+ e1000_list.next = dev;
+ cprintf("bring up e1000 device: %04x %08x\n", addr, id.join);
+
+ return 0;
+
+ err2:
+ rgmp_memremap((uintptr_t)omem, size);
+ free(omem);
+ err1:
+ rgmp_free_irq(irq, &dev->int_desc);
+ err0:
+ rgmp_memunmap(mmio_base, mmio_size);
+ error:
+ kfree(dev);
+ cprintf("e1000 device probe fail: %d\n", err);
+ return err;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+void e1000_mod_init(void)
+{
+ pci_probe_device(e1000_id_table, e1000_probe);
+}
+
+void e1000_mod_exit(void)
+{
+ uint32_t size;
+ struct e1000_dev *dev;
+
+ size = CONFIG_E1000_N_TX_DESC * sizeof(struct tx_desc) +
+ CONFIG_E1000_N_TX_DESC * CONFIG_E1000_BUFF_SIZE +
+ CONFIG_E1000_N_RX_DESC * sizeof(struct rx_desc) +
+ CONFIG_E1000_N_RX_DESC * CONFIG_E1000_BUFF_SIZE;
+ size = ROUNDUP(size, PGSIZE);
+
+ for (dev=e1000_list.next; dev!=NULL; dev=dev->next) {
+ netdev_unregister(&dev->uip_dev);
+ e1000_reset(dev);
+ wd_delete(dev->txpoll);
+ wd_delete(dev->txtimeout);
+ rgmp_memremap((uintptr_t)dev->tx_ring.desc, size);
+ free(dev->tx_ring.desc);
+ rgmp_free_irq(dev->irq, &dev->int_desc);
+ rgmp_memunmap((uintptr_t)dev->io_mem_base, dev->mem_size);
+ kfree(dev);
+ }
+
+ e1000_list.next = NULL;
+}
diff --git a/nuttx/drivers/net/e1000.h b/nuttx/drivers/net/e1000.h
new file mode 100644
index 000000000..6614ad77e
--- /dev/null
+++ b/nuttx/drivers/net/e1000.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+ * drivers/net/e1000.h
+ *
+ * Copyright (C) 2011 Yu Qiang. All rights reserved.
+ * Author: Yu Qiang <yuq825@gmail.com>
+ *
+ * This file is a part of NuttX:
+ *
+ * Copyright (C) 2011 Gregory Nutt. 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_NET_E1000_H
+#define __DRIVERS_NET_E1000_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <rgmp/types.h>
+#include <rgmp/trap.h>
+#include <rgmp/arch/arch.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/************** PCI ID ***************/
+
+#define INTEL_VENDERID 0x8086
+#define E1000_82573L 0x109a
+#define E1000_82540EM 0x100e
+#define E1000_82574L 0x10d3
+#define E1000_82567LM 0x10f5
+#define E1000_82541PI 0x107c
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+enum e1000_registers {
+ E1000_CTRL = 0x0000, // Device Control
+ E1000_STATUS = 0x0008, // Device Status
+ E1000_CTRL_EXT = 0x0018, // Device Control Extension
+ E1000_FCAL = 0x0028, // Flow Control Address Low
+ E1000_FCAH = 0x002C, // Flow Control Address High
+ E1000_FCT = 0x0030, // Flow Control Type
+ E1000_ICR = 0x00C0, // Interrupt Cause Read
+ E1000_ICS = 0x00C8, // Interrupt Cause Set
+ E1000_IMS = 0x00D0, // Interrupt Mask Set
+ E1000_IMC = 0x00D8, // Interrupt Mask Clear
+ E1000_RCTL = 0x0100, // Receive Control
+ E1000_FCTTV = 0x0170, // Flow Control Transmit Timer Value
+ E1000_TCTL = 0x0400, // Transmit Control
+ E1000_PBA = 0x1000, // Packet Buffer Allocation
+ E1000_FCRTL = 0x2160, // Flow Control Receive Threshold Low
+ E1000_FCRTH = 0x2168, // Flow Control Receive Threshold High
+ E1000_RDBAL = 0x2800, // Rx Descriptor Base Address Low
+ E1000_RDBAH = 0x2804, // Rx Descriptor Base Address High
+ E1000_RDLEN = 0x2808, // Rx Descriptor Length
+ E1000_RDH = 0x2810, // Rx Descriptor Head
+ E1000_RDT = 0x2818, // Rx Descriptor Tail
+ E1000_RXDCTL = 0x2828, // Rx Descriptor Control
+ E1000_TDBAL = 0x3800, // Tx Descriptor Base Address Low
+ E1000_TDBAH = 0x3804, // Tx Descriptor Base Address High
+ E1000_TDLEN = 0x3808, // Tx Descriptor Length
+ E1000_TDH = 0x3810, // Tx Descriptor Head
+ E1000_TDT = 0x3818, // Tx Descriptor Tail
+ E1000_TXDCTL = 0x3828, // Tx Descriptor Control
+ E1000_TPR = 0x40D0, // Total Packets Received
+ E1000_TPT = 0x40D4, // Total Packets Transmitted
+ E1000_RA = 0x5400, // Receive-filter Array
+};
+
+/***************** e1000 device structure *****************/
+
+struct tx_desc {
+ uint64_t base_address;
+ uint16_t packet_length;
+ uint8_t cksum_offset;
+ uint8_t desc_command;
+ uint8_t desc_status;
+ uint8_t cksum_origin;
+ uint16_t special_info;
+};
+
+struct rx_desc {
+ uint64_t base_address;
+ uint16_t packet_length;
+ uint16_t packet_cksum;
+ uint8_t desc_status;
+ uint8_t desc_errors;
+ uint16_t vlan_tag;
+};
+
+#endif
diff --git a/nuttx/drivers/net/enc28j60.c b/nuttx/drivers/net/enc28j60.c
new file mode 100644
index 000000000..d3c735b11
--- /dev/null
+++ b/nuttx/drivers/net/enc28j60.c
@@ -0,0 +1,2302 @@
+/****************************************************************************
+ * drivers/net/enc28j60.c
+ *
+ * Copyright (C) 2010-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * - ENC28J60 Data Sheet, Stand-Alone Ethernet Controller with SPI Interface,
+ * DS39662C, 2008 Microchip Technology Inc.
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#if defined(CONFIG_NET) && defined(CONFIG_ENC28J60)
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/spi.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/clock.h>
+#include <nuttx/net/enc28j60.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#include "enc28j60.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* ENC28J60 Configuration Settings:
+ *
+ * CONFIG_ENC28J60 - Enabled ENC28J60 support
+ * CONFIG_ENC28J60_SPIMODE - Controls the SPI mode
+ * CONFIG_ENC28J60_FREQUENCY - Define to use a different bus frequency
+ * CONFIG_ENC28J60_NINTERFACES - Specifies the number of physical ENC28J60
+ * devices that will be supported.
+ * CONFIG_ENC28J60_STATS - Collect network statistics
+ * CONFIG_ENC28J60_HALFDUPPLEX - Default is full duplex
+ */
+
+/* The ENC28J60 spec says that it supports SPI mode 0,0 only: "The
+ * implementation used on this device supports SPI mode 0,0 only. In
+ * addition, the SPI port requires that SCK be at Idle in a low state;
+ * selectable clock polarity is not supported." However, sometimes you
+ * need to tinker with these things.
+ */
+
+#ifndef CONFIG_ENC28J60_SPIMODE
+# define CONFIG_ENC28J60_SPIMODE SPIDEV_MODE0
+#endif
+
+/* CONFIG_ENC28J60_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_ENC28J60_NINTERFACES
+# define CONFIG_ENC28J60_NINTERFACES 1
+#endif
+
+/* CONFIG_NET_BUFSIZE must always be defined */
+
+#if !defined(CONFIG_NET_BUFSIZE) && (CONFIG_NET_BUFSIZE <= MAX_FRAMELEN)
+# error "CONFIG_NET_BUFSIZE is not valid for the ENC28J60"
+#endif
+
+/* We need to have the work queue to handle SPI interrupts */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE) && !defined(CONFIG_SPI_OWNBUS)
+# error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
+#endif
+
+/* CONFIG_ENC28J60_DUMPPACKET will dump the contents of each packet to the console. */
+
+#ifdef CONFIG_ENC28J60_DUMPPACKET
+# define enc_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+# define enc_dumppacket(m,a,n)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define ENC_WDDELAY (1*CLK_TCK)
+#define ENC_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define ENC_TXTIMEOUT (60*CLK_TCK)
+
+/* Poll timeout */
+
+#define ENC_POLLTIMEOUT MSEC2TICK(50)
+
+/* Packet Memory ************************************************************/
+
+/* Packet memory layout */
+
+#define ALIGNED_BUFSIZE ((CONFIG_NET_BUFSIZE + 255) & ~255)
+
+#define PKTMEM_TX_START 0x0000 /* Start TX buffer at 0 */
+#define PKTMEM_TX_ENDP1 ALIGNED_BUFSIZE /* Allow TX buffer for one frame + */
+#define PKTMEM_RX_START PKTMEM_TX_ENDP1 /* Followed by RX buffer */
+#define PKTMEM_RX_END PKTMEM_END /* RX buffer goes to the end of SRAM */
+
+/* Misc. Helper Macros ******************************************************/
+
+#define enc_rdgreg(priv,ctrlreg) \
+ enc_rdgreg2(priv, ENC_RCR | GETADDR(ctrlreg))
+#define enc_wrgreg(priv,ctrlreg,wrdata) \
+ enc_wrgreg2(priv, ENC_WCR | GETADDR(ctrlreg), wrdata)
+#define enc_bfcgreg(priv,ctrlreg,clrbits) \
+ enc_wrgreg2(priv, ENC_BFC | GETADDR(ctrlreg), clrbits)
+#define enc_bfsgreg(priv,ctrlreg,setbits) \
+ enc_wrgreg2(priv, ENC_BFS | GETADDR(ctrlreg), setbits)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)priv->dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The state of the interface */
+
+enum enc_state_e
+{
+ ENCSTATE_UNIT = 0, /* The interface is in an unknown state */
+ ENCSTATE_DOWN, /* The interface is down */
+ ENCSTATE_UP /* The interface is up */
+};
+
+/* The enc_driver_s encapsulates all state information for a single hardware
+ * interface
+ */
+
+struct enc_driver_s
+{
+ /* Device control */
+
+ uint8_t ifstate; /* Interface state: See ENCSTATE_* */
+ uint8_t bank; /* Currently selected bank */
+#ifndef CONFIG_SPI_OWNBUS
+ uint8_t lockcount; /* Avoid recursive locks */
+#endif
+ uint16_t nextpkt; /* Next packet address */
+ FAR const struct enc_lower_s *lower; /* Low-level MCU-specific support */
+
+ /* Timing */
+
+ WDOG_ID txpoll; /* TX poll timer */
+ WDOG_ID txtimeout; /* TX timeout timer */
+
+ /* If we don't own the SPI bus, then we cannot do SPI accesses from the
+ * interrupt handler.
+ */
+
+#ifndef CONFIG_SPI_OWNBUS
+ struct work_s work; /* Work queue support */
+#endif
+
+ /* This is the contained SPI driver intstance */
+
+ FAR struct spi_dev_s *spi;
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s dev; /* Interface understood by uIP */
+
+ /* Statistics */
+
+#ifdef CONFIG_ENC28J60_STATS
+ struct enc_stats_s stats;
+#endif
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct enc_driver_s g_enc28j60[CONFIG_ENC28J60_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Low-level SPI helpers */
+
+static inline void enc_configspi(FAR struct spi_dev_s *spi);
+#ifdef CONFIG_SPI_OWNBUS
+static inline void enc_select(FAR struct enc_driver_s *priv);
+static inline void enc_deselect(FAR struct enc_driver_s *priv);
+#else
+static void enc_select(FAR struct enc_driver_s *priv);
+static void enc_deselect(FAR struct enc_driver_s *priv);
+#endif
+
+/* SPI control register access */
+
+static uint8_t enc_rdgreg2(FAR struct enc_driver_s *priv, uint8_t cmd);
+static void enc_wrgreg2(FAR struct enc_driver_s *priv, uint8_t cmd,
+ uint8_t wrdata);
+static inline void enc_src(FAR struct enc_driver_s *priv);
+static void enc_setbank(FAR struct enc_driver_s *priv, uint8_t bank);
+static uint8_t enc_rdbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg);
+static void enc_wrbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg,
+ uint8_t wrdata);
+static int enc_waitbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg,
+ uint8_t bits, uint8_t value);
+
+/* SPI buffer transfers */
+
+static void enc_rdbuffer(FAR struct enc_driver_s *priv, FAR uint8_t *buffer,
+ size_t buflen);
+static void enc_wrbuffer(FAR struct enc_driver_s *priv,
+ FAR const uint8_t *buffer, size_t buflen);
+
+/* PHY register access */
+
+static uint16_t enc_rdphy(FAR struct enc_driver_s *priv, uint8_t phyaddr);
+static void enc_wrphy(FAR struct enc_driver_s *priv, uint8_t phyaddr,
+ uint16_t phydata);
+
+/* Common TX logic */
+
+static int enc_transmit(FAR struct enc_driver_s *priv);
+static int enc_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void enc_linkstatus(FAR struct enc_driver_s *priv);
+static void enc_txif(FAR struct enc_driver_s *priv);
+static void enc_txerif(FAR struct enc_driver_s *priv);
+static void enc_txerif(FAR struct enc_driver_s *priv);
+static void enc_rxerif(FAR struct enc_driver_s *priv);
+static void enc_rxdispath(FAR struct enc_driver_s *priv);
+static void enc_pktif(FAR struct enc_driver_s *priv);
+static void enc_worker(FAR void *arg);
+static int enc_interrupt(int irq, FAR void *context);
+
+/* Watchdog timer expirations */
+
+static void enc_polltimer(int argc, uint32_t arg, ...);
+static void enc_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int enc_ifup(struct uip_driver_s *dev);
+static int enc_ifdown(struct uip_driver_s *dev);
+static int enc_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int enc_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int enc_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/* Initialization */
+
+static void enc_pwrsave(FAR struct enc_driver_s *priv);
+static void enc_pwrfull(FAR struct enc_driver_s *priv);
+static void enc_setmacaddr(FAR struct enc_driver_s *priv);
+static int enc_reset(FAR struct enc_driver_s *priv);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: enc_configspi
+ *
+ * Description:
+ * Configure the SPI for use with the ENC28J60
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void enc_configspi(FAR struct spi_dev_s *spi)
+{
+ /* Configure SPI for the ENC28J60. But only if we own the SPI bus.
+ * Otherwise, don't bother because it might change.
+ */
+
+#ifdef CONFIG_SPI_OWNBUS
+ SPI_SETMODE(spi, CONFIG_ENC28J60_SPIMODE);
+ SPI_SETBITS(spi, 8);
+#ifdef CONFIG_ENC28J60_FREQUENCY
+ SPI_SETFREQUENCY(spi, CONFIG_ENC28J60_FREQUENCY)
+#endif
+#endif
+}
+
+/****************************************************************************
+ * Function: enc_select
+ *
+ * Description:
+ * Select the SPI, locking and re-configuring if necessary
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void enc_select(FAR struct enc_driver_s *priv)
+{
+ /* We own the SPI bus, so just select the chip */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);
+}
+#else
+static void enc_select(FAR struct enc_driver_s *priv)
+{
+ /* Lock the SPI bus in case there are multiple devices competing for the SPI
+ * bus. First check if we already hold the lock.
+ */
+
+ if (priv->lockcount > 0)
+ {
+ /* Yes... just increment the lock count. In this case, we know
+ * that the bus has already been configured for the ENC28J60.
+ */
+
+ DEBUGASSERT(priv->lockcount < 255);
+ priv->lockcount++;
+ }
+ else
+ {
+ /* No... take the lock and set the lock count to 1 */
+
+ DEBUGASSERT(priv->lockcount == 0);
+ SPI_LOCK(priv->spi, true);
+ priv->lockcount = 1;
+
+ /* Now make sure that the SPI bus is configured for the ENC28J60 (it
+ * might have gotten configured for a different device while unlocked)
+ */
+
+ SPI_SETMODE(priv->spi, CONFIG_ENC28J60_SPIMODE);
+ SPI_SETBITS(priv->spi, 8);
+#ifdef CONFIG_ENC28J60_FREQUENCY
+ SPI_SETFREQUENCY(priv->spi, CONFIG_ENC28J60_FREQUENCY);
+#endif
+ }
+
+ /* Select ENC28J60 chip. */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_deselect
+ *
+ * Description:
+ * De-select the SPI
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void enc_deselect(FAR struct enc_driver_s *priv)
+{
+ /* We own the SPI bus, so just de-select the chip */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+}
+#else
+static void enc_deselect(FAR struct enc_driver_s *priv)
+{
+ /* De-select ENC28J60 chip. */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+
+ /* And relinquishthe lock on the bus. If the lock count is > 1 then we
+ * are in a nested lock and we only need to decrement the lock cound.
+ */
+
+ if (priv->lockcount <= 1)
+ {
+ DEBUGASSERT(priv->lockcount == 1);
+ SPI_LOCK(priv->spi, false);
+ priv->lockcount = 0;
+ }
+ else
+ {
+ priv->lockcount--;
+ }
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_rdgreg2
+ *
+ * Description:
+ * Read a global register (EIE, EIR, ESTAT, ECON2, or ECON1). The cmd
+ * include the CMD 'OR'd with the the global address register.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * cmd - The full command to received (cmd | address)
+ *
+ * Returned Value:
+ * The value read from the register
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint8_t enc_rdgreg2(FAR struct enc_driver_s *priv, uint8_t cmd)
+{
+ uint8_t rddata;
+
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Send the read command and collect the data. The sequence requires
+ * 16-clocks: 8 to clock out the cmd + 8 to clock in the data.
+ */
+
+ (void)SPI_SEND(priv->spi, cmd); /* Clock out the command */
+ rddata = SPI_SEND(priv->spi, 0); /* Clock in the data */
+
+ /* De-select ENC28J60 chip */
+
+ enc_deselect(priv);
+ return rddata;
+}
+
+/****************************************************************************
+ * Function: enc_wrgreg2
+ *
+ * Description:
+ * Write to a global register (EIE, EIR, ESTAT, ECON2, or ECON1). The cmd
+ * include the CMD 'OR'd with the the global address register.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * cmd - The full command to received (cmd | address)
+ * wrdata - The data to send
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_wrgreg2(FAR struct enc_driver_s *priv, uint8_t cmd,
+ uint8_t wrdata)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Send the write command and data. The sequence requires 16-clocks:
+ * 8 to clock out the cmd + 8 to clock out the data.
+ */
+
+ (void)SPI_SEND(priv->spi, cmd); /* Clock out the command */
+ (void)SPI_SEND(priv->spi, wrdata); /* Clock out the data */
+
+ /* De-select ENC28J60 chip. */
+
+ enc_deselect(priv);
+}
+
+/****************************************************************************
+ * Function: enc_src
+ *
+ * Description:
+ * Send the single byte system reset command (SRC).
+ *
+ * "The System Reset Command (SRC) allows the host controller to issue a
+ * System Soft Reset command. Unlike other SPI commands, the SRC is
+ * only a single byte command and does not operate on any register. The
+ * command is started by pulling the CS pin low. The SRC opcode is the
+ * sent, followed by a 5-bit Soft Reset command constant of 1Fh. The
+ * SRC operation is terminated by raising the CS pin."
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void enc_src(FAR struct enc_driver_s *priv)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Send the system reset command. */
+
+ (void)SPI_SEND(priv->spi, ENC_SRC);
+
+ /* Check CLKRDY bit to see when the reset is complete. There is an errata
+ * that says the CLKRDY may be invalid. We'll wait a couple of msec to
+ * workaround this condition.
+ *
+ * Also, "After a System Reset, all PHY registers should not be read or
+ * written to until at least 50 µs have passed since the Reset has ended.
+ * All registers will revert to their Reset default values. The dual
+ * port buffer memory will maintain state throughout the System Reset."
+ */
+
+ up_mdelay(2);
+ /* while ((enc_rdgreg(priv, ENC_ESTAT) & ESTAT_CLKRDY) != 0); */
+
+ /* De-select ENC28J60 chip. */
+
+ enc_deselect(priv);
+}
+
+/****************************************************************************
+ * Function: enc_setbank
+ *
+ * Description:
+ * Set the bank for these next control register access.
+ *
+ * Assumption:
+ * The caller has exclusive access to the SPI bus
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * bank - The bank to select (0-3)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_setbank(FAR struct enc_driver_s *priv, uint8_t bank)
+{
+ /* Check if the bank setting has changed*/
+
+ if (bank != priv->bank)
+ {
+ /* Select bank 0 (just so that all of the bits are cleared) */
+
+ enc_bfcgreg(priv, ENC_ECON1, ECON1_BSEL_MASK);
+
+ /* Then OR in bits to get the correct bank */
+
+ if (bank != 0)
+ {
+ enc_bfsgreg(priv, ENC_ECON1, (bank << ECON1_BSEL_SHIFT));
+ }
+
+ /* Then remember the bank setting */
+
+ priv->bank = bank;
+ }
+}
+
+/****************************************************************************
+ * Function: enc_rdbreg
+ *
+ * Description:
+ * Read from a banked control register using the RCR command.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to read
+ *
+ * Returned Value:
+ * The byte read from the banked register
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint8_t enc_rdbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg)
+{
+ uint8_t rddata;
+
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Set the bank */
+
+ enc_setbank(priv, GETBANK(ctrlreg));
+
+ /* Send the RCR command and collect the data. How we collect the data
+ * depends on if this is a PHY/CAN or not. The normal sequence requires
+ * 16-clocks: 8 to clock out the cmd and 8 to clock in the data.
+ */
+
+ (void)SPI_SEND(priv->spi, ENC_RCR | GETADDR(ctrlreg)); /* Clock out the command */
+ if (ISPHYMAC(ctrlreg))
+ {
+ /* The PHY/MAC sequence requires 24-clocks: 8 to clock out the cmd,
+ * 8 dummy bits, and 8 to clock in the PHY/MAC data.
+ */
+
+ (void)SPI_SEND(priv->spi, 0); /* Clock in the dummy byte */
+ }
+
+ rddata = SPI_SEND(priv->spi, 0); /* Clock in the data */
+
+ /* De-select ENC28J60 chip */
+
+ enc_deselect(priv);
+ return rddata;
+}
+
+/****************************************************************************
+ * Function: enc_wrbreg
+ *
+ * Description:
+ * Write to a banked control register using the WCR command. Unlike
+ * reading, this same SPI sequence works for normal, MAC, and PHY
+ * registers.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to write
+ * wrdata - The data to send
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_wrbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg,
+ uint8_t wrdata)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Set the bank */
+
+ enc_setbank(priv, GETBANK(ctrlreg));
+
+ /* Send the WCR command and data. The sequence requires 16-clocks:
+ * 8 to clock out the cmd + 8 to clock out the data.
+ */
+
+ (void)SPI_SEND(priv->spi, ENC_WCR | GETADDR(ctrlreg)); /* Clock out the command */
+ (void)SPI_SEND(priv->spi, wrdata); /* Clock out the data */
+
+ /* De-select ENC28J60 chip. */
+
+ enc_deselect(priv);
+}
+
+/****************************************************************************
+ * Function: enc_waitbreg
+ *
+ * Description:
+ * Wait until banked register bit(s) take a specific value (or a timeout
+ * occurs).
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to check
+ * bits - The bits to check (a mask)
+ * value - The value of the bits to return (value under mask)
+ *
+ * Returned Value:
+ * OK on success, negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_waitbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg,
+ uint8_t bits, uint8_t value)
+{
+ uint32_t start = clock_systimer();
+ uint32_t elapsed;
+ uint8_t rddata;
+
+ /* Loop until the exit condition is met */
+
+ do
+ {
+ /* Read the byte from the requested banked register */
+
+ rddata = enc_rdbreg(priv, ctrlreg);
+ elapsed = clock_systimer() - start;
+ }
+ while ((rddata & bits) != value || elapsed > ENC_POLLTIMEOUT);
+
+ return (rddata & bits) == value ? -ETIMEDOUT : OK;
+}
+
+/****************************************************************************
+ * Function: enc_rdbuffer
+ *
+ * Description:
+ * Read a buffer of data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * buffer - A pointer to the buffer to read into
+ * buflen - The number of bytes to read
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Read pointer is set to the correct address
+ *
+ ****************************************************************************/
+
+static void enc_rdbuffer(FAR struct enc_driver_s *priv, FAR uint8_t *buffer,
+ size_t buflen)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Send the read buffer memory command (ignoring the response) */
+
+ (void)SPI_SEND(priv->spi, ENC_RBM);
+
+ /* Then read the buffer data */
+
+ SPI_RECVBLOCK(priv->spi, buffer, buflen);
+
+ /* De-select ENC28J60 chip. */
+
+ enc_deselect(priv);
+}
+
+/****************************************************************************
+ * Function: enc_wrbuffer
+ *
+ * Description:
+ * Write a buffer of data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * buffer - A pointer to the buffer to write from
+ * buflen - The number of bytes to write
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Read pointer is set to the correct address
+ *
+ ****************************************************************************/
+
+static void enc_wrbuffer(FAR struct enc_driver_s *priv,
+ FAR const uint8_t *buffer, size_t buflen)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Send the write buffer memory command (ignoring the response) */
+
+ (void)SPI_SEND(priv->spi, ENC_WBM);
+
+ /* Then send the buffer */
+
+ SPI_SNDBLOCK(priv->spi, buffer, buflen);
+
+ /* De-select ENC28J60 chip. */
+
+ enc_deselect(priv);
+}
+
+/****************************************************************************
+ * Function: enc_rdphy
+ *
+ * Description:
+ * Read 16-bits of PHY data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * phyaddr - The PHY register address
+ *
+ * Returned Value:
+ * 16-bit value read from the PHY
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint16_t enc_rdphy(FAR struct enc_driver_s *priv, uint8_t phyaddr)
+{
+ uint16_t data = 0;
+
+ /* Set the PHY address (and start the PHY read operation) */
+
+ enc_wrbreg(priv, ENC_MIREGADR, phyaddr);
+ enc_wrbreg(priv, ENC_MICMD, MICMD_MIIRD);
+
+ /* Wait until the PHY read completes */
+
+ if (enc_waitbreg(priv, ENC_MISTAT, MISTAT_BUSY, 0x00) == OK);
+ {
+ /* Terminate reading */
+
+ enc_wrbreg(priv, ENC_MICMD, 0x00);
+
+ /* Get the PHY data */
+
+ data = (uint16_t)enc_rdbreg(priv, ENC_MIRDL);
+ data |= (uint16_t)enc_rdbreg(priv, ENC_MIRDH) << 8;
+ }
+
+ return data;
+}
+
+/****************************************************************************
+ * Function: enc_wrphy
+ *
+ * Description:
+ * write 16-bits of PHY data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * phyaddr - The PHY register address
+ * phydata - 16-bit data to write to the PHY
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_wrphy(FAR struct enc_driver_s *priv, uint8_t phyaddr,
+ uint16_t phydata)
+{
+ /* Set the PHY register address */
+
+ enc_wrbreg(priv, ENC_MIREGADR, phyaddr);
+
+ /* Write the PHY data */
+
+ enc_wrbreg(priv, ENC_MIWRL, phydata);
+ enc_wrbreg(priv, ENC_MIWRH, phydata >> 8);
+
+ /* Wait until the PHY write completes */
+
+ enc_waitbreg(priv, ENC_MISTAT, MISTAT_BUSY, 0x00);
+}
+
+/****************************************************************************
+ * Function: enc_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from:
+ *
+ * - pkif interrupt when an application responds to the receipt of data
+ * by trying to send something, or
+ * - From watchdog based polling.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_transmit(FAR struct enc_driver_s *priv)
+{
+ uint16_t txend;
+
+ /* Increment statistics */
+
+ nllvdbg("Sending packet, pktlen: %d\n", priv->dev.d_len);
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.txrequests++;
+#endif
+
+ /* Verify that the hardware is ready to send another packet. The driver
+ * starts a transmission process by setting ECON1.TXRTS. When the packet is
+ * finished transmitting or is aborted due to an error/cancellation, the
+ * ECON1.TXRTS bit will be cleared.
+ *
+ * NOTE: If we got here, then we have committed to sending a packet.
+ * higher level logic must have assured that (1) there is no transmission
+ * in progress, and that (2) TX-related interrupts are disabled.
+ */
+
+ DEBUGASSERT((enc_rdgreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0);
+
+ /* Send the packet: address=priv->dev.d_buf, length=priv->dev.d_len */
+
+ enc_dumppacket("Transmit Packet", priv->dev.d_buf, priv->dev.d_len);
+
+ /* Reset the write pointer to start of transmit buffer */
+
+ enc_wrbreg(priv, ENC_EWRPTL, PKTMEM_TX_START & 0xff);
+ enc_wrbreg(priv, ENC_EWRPTH, PKTMEM_TX_START >> 8);
+
+ /* Set the TX End pointer based on the size of the packet to send */
+
+ txend = PKTMEM_TX_START + priv->dev.d_len;
+ enc_wrbreg(priv, ENC_ETXNDL, txend & 0xff);
+ enc_wrbreg(priv, ENC_ETXNDH, txend >> 8);
+
+ /* Write the per-packet control byte into the transmit buffer */
+
+ enc_wrgreg(priv, ENC_WBM, 0x00);
+
+ /* Copy the packet itself into the transmit buffer */
+
+ enc_wrbuffer(priv, priv->dev.d_buf, priv->dev.d_len);
+
+ /* Set TXRTS to send the packet in the transmit buffer */
+
+ enc_bfsgreg(priv, ENC_ECON1, ECON1_TXRTS);
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer). Note:
+ * Is there a race condition. Could the TXIF interrupt occur before
+ * the timer is started?
+ */
+
+ (void)wd_start(priv->txtimeout, ENC_TXTIMEOUT, enc_txtimeout, 1, (uint32_t)priv);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: enc_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timesout and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_uiptxpoll(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+
+ /* If the polling resulted in data that should be sent out on the network,
+ * the field d_len is set to a value > 0.
+ */
+
+ nllvdbg("Poll result: d_len=%d\n", priv->dev.d_len);
+ if (priv->dev.d_len > 0)
+ {
+ uip_arp_out(&priv->dev);
+ enc_transmit(priv);
+
+ /* Stop the poll now because we can queue only one packet */
+
+ return -EBUSY;
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: enc_linkstatus
+ *
+ * Description:
+ * The current link status can be obtained from the PHSTAT1.LLSTAT or
+ * PHSTAT2.LSTAT.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_linkstatus(FAR struct enc_driver_s *priv)
+{
+#if 0
+ uint16_t regval = enc_rdphy(priv, ENC_PHSTAT2);
+ priv->duplex = ((regval & PHSTAT2_DPXSTAT) != 0);
+ priv->carrier = ((regval & PHSTAT2_LSTAT) != 0);
+#endif
+}
+
+/****************************************************************************
+ * Function: enc_txif
+ *
+ * Description:
+ * An TXIF interrupt was received indicating that the last TX packet(s) is
+ * done
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_txif(FAR struct enc_driver_s *priv)
+{
+ /* Update statistics */
+
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.txifs++;
+ if (enc_rdgreg(priv, ENC_ESTAT) & ESTAT_TXABRT)
+ {
+ priv->stats.txabrts++;
+ }
+#endif
+
+ /* Clear the request to send bit */
+
+ enc_bfcgreg(priv, ENC_ECON1, ECON1_TXRTS);
+
+ /* If no further xmits are pending, then cancel the TX timeout */
+
+ wd_cancel(priv->txtimeout);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&priv->dev, enc_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: enc_txerif
+ *
+ * Description:
+ * An TXERIF interrupt was received indicating that a TX abort has occurred.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_txerif(FAR struct enc_driver_s *priv)
+{
+ /* Update statistics */
+
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.txerifs++;
+#endif
+
+ /* Reset TX */
+
+ enc_bfsgreg(priv, ENC_ECON1, ECON1_TXRST);
+ enc_bfcgreg(priv, ENC_ECON1, ECON1_TXRST | ECON1_TXRTS);
+
+ /* Here we really should re-transmit (I fact, if we want half duplex to
+ * work right, then it is necessary to do this!):
+ *
+ * 1. Read the TSV:
+ * - Read ETXNDL to get the end pointer
+ * - Read 7 bytes from that pointer + 1 using ENC_RMB.
+ * 2. Determine if we need to retransmit. Check the LATE COLLISION bit, if
+ * set, then we need to transmit.
+ * 3. Retranmit by resetting ECON1_TXRTS.
+ */
+
+#ifdef CONFIG_ENC28J60_HALFDUPLEX
+# error "Missing logic for half duplex"
+#endif
+}
+
+/****************************************************************************
+ * Function: enc_rxerif
+ *
+ * Description:
+ * An RXERIF interrupt was received indicating that the last TX packet(s) is
+ * done
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_rxerif(FAR struct enc_driver_s *priv)
+{
+ /* Update statistics */
+
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.rxerifs++;
+#endif
+}
+
+/****************************************************************************
+ * Function: enc_rxdispath
+ *
+ * Description:
+ * Give the newly received packet to uIP.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_rxdispath(FAR struct enc_driver_s *priv)
+{
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ nllvdbg("IP packet received (%02x)\n", BUF->type);
+ uip_arp_ipin(&priv->dev);
+ uip_input(&priv->dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ uip_arp_out(&priv->dev);
+ enc_transmit(priv);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP))
+ {
+ nllvdbg("ARP packet received (%02x)\n", BUF->type);
+ uip_arp_arpin(&priv->dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ enc_transmit(priv);
+ }
+ }
+ else
+ {
+ nlldbg("Unsupported packet type dropped (%02x)\n", htons(BUF->type));
+ }
+}
+
+/****************************************************************************
+ * Function: enc_pktif
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_pktif(FAR struct enc_driver_s *priv)
+{
+ uint8_t rsv[6];
+ uint16_t pktlen;
+ uint16_t rxstat;
+
+ /* Update statistics */
+
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.pktifs++;
+#endif
+
+ /* Set the read pointer to the start of the received packet */
+
+ DEBUGASSERT(priv->nextpkt <= PKTMEM_RX_END);
+ enc_wrbreg(priv, ENC_ERDPTL, (priv->nextpkt));
+ enc_wrbreg(priv, ENC_ERDPTH, (priv->nextpkt) >> 8);
+
+ /* Read the next packet pointer and the 4 byte read status vector (RSV)
+ * at the beginning of the received packet
+ */
+
+ enc_rdbuffer(priv, rsv, 6);
+
+ /* Decode the new next packet pointer, and the RSV. The
+ * RSV is encoded as:
+ *
+ * Bits 0-15: Indicates length of the received frame. This includes the
+ * destination address, source address, type/length, data,
+ * padding and CRC fields. This field is stored in little-
+ * endian format.
+ * Bits 16-31: Bit encoded RX status.
+ */
+
+ priv->nextpkt = (uint16_t)rsv[1] << 8 | (uint16_t)rsv[0];
+ pktlen = (uint16_t)rsv[3] << 8 | (uint16_t)rsv[2];
+ rxstat = (uint16_t)rsv[5] << 8 | (uint16_t)rsv[4];
+ nllvdbg("Receiving packet, pktlen: %d\n", pktlen);
+
+ /* Check if the packet was received OK */
+
+ if ((rxstat & RXSTAT_OK) == 0)
+ {
+ nlldbg("ERROR: RXSTAT: %04x\n", rxstat);
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.rxnotok++;
+#endif
+ }
+
+ /* Check for a usable packet length (4 added for the CRC) */
+
+ else if (pktlen > (CONFIG_NET_BUFSIZE + 4) || pktlen <= (UIP_LLH_LEN + 4))
+ {
+ nlldbg("Bad packet size dropped (%d)\n", pktlen);
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.rxpktlen++;
+#endif
+ }
+
+ /* Otherwise, read and process the packet */
+
+ else
+ {
+ /* Save the packet length (without the 4 byte CRC) in priv->dev.d_len*/
+
+ priv->dev.d_len = pktlen - 4;
+
+ /* Copy the data data from the receive buffer to priv->dev.d_buf */
+
+ enc_rdbuffer(priv, priv->dev.d_buf, priv->dev.d_len);
+ enc_dumppacket("Received Packet", priv->ld_dev.d_buf, priv->ld_dev.d_len);
+
+ /* Dispatch the packet to uIP */
+
+ enc_rxdispath(priv);
+ }
+
+ /* Move the RX read pointer to the start of the next received packet.
+ * This frees the memory we just read.
+ */
+
+ enc_wrbreg(priv, ENC_ERXRDPTL, (priv->nextpkt));
+ enc_wrbreg(priv, ENC_ERXRDPTH, (priv->nextpkt) >> 8);
+
+ /* Decrement the packet counter indicate we are done with this packet */
+
+ enc_bfsgreg(priv, ENC_ECON2, ECON2_PKTDEC);
+}
+
+/****************************************************************************
+ * Function: enc_worker
+ *
+ * Description:
+ * Perform interrupt handling logic outside of the interrupt handler (on
+ * the work queue thread).
+ *
+ * Parameters:
+ * arg - The reference to the driver structure (case to void*)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_worker(FAR void *arg)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+ uint8_t eir;
+
+ DEBUGASSERT(priv);
+
+ /* Disable further interrupts by clearing the global interrup enable bit */
+
+ enc_bfcgreg(priv, ENC_EIE, EIE_INTIE);
+
+ /* Loop until all interrupts have been processed (EIR==0). Note that
+ * there is no infinite loop check... if there are always pending interrupts,
+ * we are just broken.
+ */
+
+ while ((eir = enc_rdgreg(priv, ENC_EIR) & EIR_ALLINTS) != 0)
+ {
+ /* Handle interrupts according to interrupt register register bit
+ * settings
+ *
+ * DMAIF: The DMA interrupt indicates that the DMA module has completed
+ * its memory copy or checksum calculation. Additionally, this interrupt
+ * will be caused if the host controller cancels a DMA operation by
+ * manually clearing the DMAST bit. Once set, DMAIF can only be cleared
+ * by the host controller or by a Reset condition.
+ */
+
+ if ((eir & EIR_DMAIF) != 0) /* DMA interrupt */
+ {
+ /* Not used by this driver. Just clear the interrupt request. */
+
+ enc_bfcgreg(priv, ENC_EIR, EIR_DMAIF);
+ }
+
+ /* LINKIF: The LINKIF indicates that the link status has changed.
+ * The actual current link status can be obtained from the
+ * PHSTAT1.LLSTAT or PHSTAT2.LSTAT. Unlike other interrupt sources, the
+ * link status change interrupt is created in the integrated PHY
+ * module.
+ *
+ * To receive it, the host controller must set the PHIE.PLNKIE and
+ * PGEIE bits. After setting the two PHY interrupt enable bits, the
+ * LINKIF bit will then shadow the contents of the PHIR.PGIF bit.
+ *
+ * Once LINKIF is set, it can only be cleared by the host controller or
+ * by a Reset. The LINKIF bit is read-only. Performing an MII read on
+ * the PHIR register will clear the LINKIF, PGIF and PLNKIF bits
+ * automatically and allow for future link status change interrupts.
+ */
+
+ if ((eir & EIR_LINKIF) != 0) /* Link change interrupt */
+ {
+ enc_linkstatus(priv); /* Get current link status */
+ enc_rdphy(priv, ENC_PHIR); /* Clear the LINKIF interrupt */
+ }
+
+ /* TXIF: The Transmit Interrupt Flag (TXIF) is used to indicate that
+ * the requested packet transmission has ended. Upon transmission
+ * completion, abort or transmission cancellation by the host
+ * controller, the EIR.TXIF flag will be set to 1.
+ *
+ * Once TXIF is set, it can only be cleared by the host controller
+ * or by a Reset condition. Once processed, the host controller should
+ * use the BFC command to clear the EIR.TXIF bit.
+ */
+
+ if ((eir & EIR_TXIF) != 0) /* Transmit interrupt */
+ {
+ enc_txif(priv); /* Handle TX completion */
+ enc_bfcgreg(priv, ENC_EIR, EIR_TXIF); /* Clear the TXIF interrupt */
+ }
+
+ /* TXERIF: The Transmit Error Interrupt Flag (TXERIF) is used to
+ * indicate that a transmit abort has occurred. An abort can occur
+ * because of any of the following:
+ *
+ * 1. Excessive collisions occurred as defined by the Retransmission
+ * Maximum (RETMAX) bits in the MACLCON1 register.
+ * 2. A late collision occurred as defined by the Collision Window
+ * (COLWIN) bits in the MACLCON2 register.
+ * 3. A collision after transmitting 64 bytes occurred (ESTAT.LATECOL
+ * set).
+ * 4. The transmission was unable to gain an opportunity to transmit
+ * the packet because the medium was constantly occupied for too long.
+ * The deferral limit (2.4287 ms) was reached and the MACON4.DEFER bit
+ * was clear.
+ * 5. An attempt to transmit a packet larger than the maximum frame
+ * length defined by the MAMXFL registers was made without setting
+ * the MACON3.HFRMEN bit or per packet POVERRIDE and PHUGEEN bits.
+ *
+ * Upon any of these conditions, the EIR.TXERIF flag is set to 1. Once
+ * set, it can only be cleared by the host controller or by a Reset
+ * condition.
+ *
+ * After a transmit abort, the TXRTS bit will be cleared, the
+ * ESTAT.TXABRT bit will be set and the transmit status vector will be
+ * written at ETXND + 1. The MAC will not automatically attempt to
+ * retransmit the packet. The host controller may wish to read the
+ * transmit status vector and LATECOL bit to determine the cause of
+ * the abort. After determining the problem and solution, the host
+ * controller should clear the LATECOL (if set) and TXABRT bits so
+ * that future aborts can be detected accurately.
+ *
+ * In Full-Duplex mode, condition 5 is the only one that should cause
+ * this interrupt. Collisions and other problems related to sharing
+ * the network are not possible on full-duplex networks. The conditions
+ * which cause the transmit error interrupt meet the requirements of the
+ * transmit interrupt. As a result, when this interrupt occurs, TXIF
+ * will also be simultaneously set.
+ */
+
+ if ((eir & EIR_TXERIF) != 0) /* Transmit Error Interrupts */
+ {
+ enc_txerif(priv); /* Handle the TX error */
+ enc_bfcgreg(priv, ENC_EIR, EIR_TXERIF); /* Clear the TXERIF interrupt */
+ }
+
+ /* PKTIF The Receive Packet Pending Interrupt Flag (PKTIF) is used to
+ * indicate the presence of one or more data packets in the receive
+ * buffer and to provide a notification means for the arrival of new
+ * packets. When the receive buffer has at least one packet in it,
+ * EIR.PKTIF will be set. In other words, this interrupt flag will be
+ * set anytime the Ethernet Packet Count register (EPKTCNT) is non-zero.
+ *
+ * The PKTIF bit can only be cleared by the host controller or by a Reset
+ * condition. In order to clear PKTIF, the EPKTCNT register must be
+ * decremented to 0. If the last data packet in the receive buffer is
+ * processed, EPKTCNT will become zero and the PKTIF bit will automatically
+ * be cleared.
+ */
+
+ /* Ignore PKTIF because is unreliable. Use EPKTCNT instead */
+ /* if ((eir & EIR_PKTIF) != 0) */
+ {
+ uint8_t pktcnt = enc_rdbreg(priv, ENC_EPKTCNT);
+ if (pktcnt > 0)
+ {
+#ifdef CONFIG_ENC28J60_STATS
+ if (pktcnt > priv->stats.maxpktcnt)
+ {
+ priv->stats.maxpktcnt = pktcnt;
+ }
+#endif
+ /* Handle packet receipt */
+
+ enc_pktif(priv);
+ }
+ }
+
+ /* RXERIF: The Receive Error Interrupt Flag (RXERIF) is used to
+ * indicate a receive buffer overflow condition. Alternately, this
+ * interrupt may indicate that too many packets are in the receive
+ * buffer and more cannot be stored without overflowing the EPKTCNT
+ * register. When a packet is being received and the receive buffer
+ * runs completely out of space, or EPKTCNT is 255 and cannot be
+ * incremented, the packet being received will be aborted (permanently
+ * lost) and the EIR.RXERIF bit will be set to 1.
+ *
+ * Once set, RXERIF can only be cleared by the host controller or by a
+ * Reset condition. Normally, upon the receive error condition, the
+ * host controller would process any packets pending from the receive
+ * buffer and then make additional room for future packets by
+ * advancing the ERXRDPT registers (low byte first) and decrementing
+ * the EPKTCNT register.
+ *
+ * Once processed, the host controller should use the BFC command to
+ * clear the EIR.RXERIF bit.
+ */
+
+ if ((eir & EIR_RXERIF) != 0) /* Receive Errror Interrupts */
+ {
+ enc_rxerif(priv); /* Handle the RX error */
+ enc_bfcgreg(priv, ENC_EIR, EIR_RXERIF); /* Clear the RXERIF interrupt */
+ }
+
+ }
+
+ /* Enable Ethernet interrupts */
+
+ enc_bfsgreg(priv, ENC_EIE, EIE_INTIE);
+}
+
+/****************************************************************************
+ * Function: enc_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_interrupt(int irq, FAR void *context)
+{
+ register FAR struct enc_driver_s *priv = &g_enc28j60[0];
+
+#ifdef CONFIG_SPI_OWNBUS
+ /* In very simple environments, we own the SPI and can do data transfers
+ * from the interrupt handler. That is actually a very bad idea in any
+ * case because it keeps interrupts disabled for a long time.
+ */
+
+ enc_worker((FAR void*)priv);
+ return OK;
+#else
+ /* In complex environments, we cannot do SPI transfers from the interrupt
+ * handler because semaphores are probably used to lock the SPI bus. In
+ * this case, we will defer processing to the worker thread. This is also
+ * much kinder in the use of system resources and is, therefore, probably
+ * a good thing to do in any event.
+ */
+
+ return work_queue(HPWORK, &priv->work, enc_worker, (FAR void *)priv, 0);
+#endif
+}
+
+/****************************************************************************
+ * Function: enc_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_txtimeout(int argc, uint32_t arg, ...)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+ int ret;
+
+ /* Increment statistics and dump debug info */
+
+ nlldbg("Tx timeout\n");
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.txtimeouts++;
+#endif
+
+ /* Then reset the hardware: Take the interface down, then bring it
+ * back up
+ */
+
+ ret = enc_ifdown(&priv->dev);
+ DEBUGASSERT(ret == OK);
+ ret = enc_ifup(&priv->dev);
+ DEBUGASSERT(ret == OK);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&priv->dev, enc_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: enc_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_polltimer(int argc, uint32_t arg, ...)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+
+ /* Verify that the hardware is ready to send another packet. The driver
+ * start a transmission process by setting ECON1.TXRTS. When the packet is
+ * finished transmitting or is aborted due to an error/cancellation, the
+ * ECON1.TXRTS bit will be cleared.
+ */
+
+ if ((enc_rdgreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0)
+ {
+ /* Yes.. update TCP timing states and poll uIP for new XMIT data. Hmmm..
+ * looks like a bug here to me. Does this mean if there is a transmit
+ * in progress, we will missing TCP time state updates?
+ */
+
+ (void)uip_timer(&priv->dev, enc_uiptxpoll, ENC_POLLHSEC);
+ }
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(priv->txpoll, ENC_WDDELAY, enc_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: enc_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_ifup(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+ int ret;
+
+ nlldbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initialize Ethernet interface, set the MAC address, and make sure that
+ * the ENC28J80 is not in power save mode.
+ */
+
+ ret = enc_reset(priv);
+ if (ret == OK)
+ {
+ enc_setmacaddr(priv);
+ enc_pwrfull(priv);
+
+ /* Enable interrupts at the ENC28J60. Interrupts are still disabled
+ * at the interrupt controller.
+ */
+
+ enc_wrphy(priv, ENC_PHIE, PHIE_PGEIE | PHIE_PLNKIE);
+ enc_bfsgreg(priv, ENC_EIE, EIE_INTIE | EIE_PKTIE);
+ enc_bfsgreg(priv, ENC_EIR, EIR_DMAIF | EIR_LINKIF | EIR_TXIF |
+ EIR_TXERIF | EIR_RXERIF | EIR_PKTIF);
+ enc_wrgreg(priv, ENC_EIE, EIE_INTIE | EIE_PKTIE | EIE_LINKIE |
+ EIE_TXIE | EIE_TXERIE | EIE_RXERIE);
+
+ /* Enable the receiver */
+
+ enc_bfsgreg(priv, ENC_ECON1, ECON1_RXEN);
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(priv->txpoll, ENC_WDDELAY, enc_polltimer, 1, (uint32_t)priv);
+
+ /* Mark the interface up and enable the Ethernet interrupt at the
+ * controller
+ */
+
+ priv->ifstate = ENCSTATE_UP;
+ priv->lower->enable(priv->lower);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Function: enc_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_ifdown(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+ irqstate_t flags;
+ int ret;
+
+ nlldbg("Taking down: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+ priv->lower->disable(priv->lower);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(priv->txpoll);
+ wd_cancel(priv->txtimeout);
+
+ /* Reset the device and leave in the power save state */
+
+ ret = enc_reset(priv);
+ enc_pwrsave(priv);
+
+ priv->ifstate = ENCSTATE_DOWN;
+ irqrestore(flags);
+ return ret;
+}
+
+/****************************************************************************
+ * Function: enc_txavail
+ *
+ * Description:
+ * Driver callback invoked when new TX data is available. This is a
+ * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ * latency.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int enc_txavail(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (priv->ifstate == ENCSTATE_UP)
+ {
+ /* Check if the hardware is ready to send another packet. The driver
+ * starts a transmission process by setting ECON1.TXRTS. When the packet is
+ * finished transmitting or is aborted due to an error/cancellation, the
+ * ECON1.TXRTS bit will be cleared.
+ */
+
+ if ((enc_rdgreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0)
+ {
+ /* The interface is up and TX is idle; poll uIP for new XMIT data */
+
+ (void)uip_poll(&priv->dev, enc_uiptxpoll);
+ }
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: enc_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int enc_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int enc_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_pwrsave
+ *
+ * Description:
+ * The ENC28J60 may be commanded to power-down via the SPI interface.
+ * When powered down, it will no longer be able to transmit and receive
+ * any packets. To maximize power savings:
+ *
+ * 1. Turn off packet reception by clearing ECON1.RXEN.
+ * 2. Wait for any in-progress packets to finish being received by
+ * polling ESTAT.RXBUSY. This bit should be clear before proceeding.
+ * 3. Wait for any current transmissions to end by confirming ECON1.TXRTS
+ * is clear.
+ * 4. Set ECON2.VRPS (if not already set).
+ * 5. Enter Sleep by setting ECON2.PWRSV. All MAC, MII and PHY registers
+ * become inaccessible as a result. Setting PWRSV also clears
+ * ESTAT.CLKRDY automatically.
+ *
+ * In Sleep mode, all registers and buffer memory will maintain their
+ * states. The ETH registers and buffer memory will still be accessible
+ * by the host controller. Additionally, the clock driver will continue
+ * to operate. The CLKOUT function will be unaffected.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_pwrsave(FAR struct enc_driver_s *priv)
+{
+ nllvdbg("Set PWRSV\n");
+
+ /* 1. Turn off packet reception by clearing ECON1.RXEN. */
+
+ enc_bfcgreg(priv, ENC_ECON1, ECON1_RXEN);
+
+ /* 2. Wait for any in-progress packets to finish being received by
+ * polling ESTAT.RXBUSY. This bit should be clear before proceeding.
+ */
+
+ if (enc_waitbreg(priv, ENC_ESTAT, ESTAT_RXBUSY, 0) == OK)
+ {
+ /* 3. Wait for any current transmissions to end by confirming
+ * ECON1.TXRTS is clear.
+ */
+
+ enc_waitbreg(priv, ENC_ECON1, ECON1_TXRTS, 0);
+
+ /* 4. Set ECON2.VRPS (if not already set). */
+ /* enc_bfsgreg(priv, ENC_ECON2, ECON2_VRPS); <-- Set in enc_reset() */
+
+ /* 5. Enter Sleep by setting ECON2.PWRSV. */
+
+ enc_bfsgreg(priv, ENC_ECON2, ECON2_PWRSV);
+ }
+}
+
+/****************************************************************************
+ * Function: enc_pwrfull
+ *
+ * Description:
+ * When normal operation is desired, the host controller must perform
+ * a slightly modified procedure:
+ *
+ * 1. Wake-up by clearing ECON2.PWRSV.
+ * 2. Wait at least 300 ìs for the PHY to stabilize. To accomplish the
+ * delay, the host controller may poll ESTAT.CLKRDY and wait for it
+ * to become set.
+ * 3. Restore receive capability by setting ECON1.RXEN.
+ *
+ * After leaving Sleep mode, there is a delay of many milliseconds
+ * before a new link is established (assuming an appropriate link
+ * partner is present). The host controller may wish to wait until
+ * the link is established before attempting to transmit any packets.
+ * The link status can be determined by polling the PHSTAT2.LSTAT bit.
+ * Alternatively, the link change interrupt may be used if it is
+ * enabled.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_pwrfull(FAR struct enc_driver_s *priv)
+{
+ nllvdbg("Clear PWRSV\n");
+
+ /* 1. Wake-up by clearing ECON2.PWRSV. */
+
+ enc_bfcgreg(priv, ENC_ECON2, ECON2_PWRSV);
+
+ /* 2. Wait at least 300 ìs for the PHY to stabilize. To accomplish the
+ * delay, the host controller may poll ESTAT.CLKRDY and wait for it to
+ * become set.
+ */
+
+ enc_waitbreg(priv, ENC_ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
+
+ /* 3. Restore receive capability by setting ECON1.RXEN.
+ *
+ * The caller will do this when it is read to receive packets
+ */
+}
+
+/****************************************************************************
+ * Function: enc_setmacaddr
+ *
+ * Description:
+ * Set the MAC address to the configured value. This is done after ifup
+ * or after a TX timeout. Note that this means that the interface must
+ * be down before configuring the MAC addr.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_setmacaddr(FAR struct enc_driver_s *priv)
+{
+ /* Program the hardware with it's MAC address (for filtering) */
+
+ enc_wrbreg(priv, ENC_MAADR1, priv->dev.d_mac.ether_addr_octet[5]);
+ enc_wrbreg(priv, ENC_MAADR2, priv->dev.d_mac.ether_addr_octet[4]);
+ enc_wrbreg(priv, ENC_MAADR3, priv->dev.d_mac.ether_addr_octet[3]);
+ enc_wrbreg(priv, ENC_MAADR4, priv->dev.d_mac.ether_addr_octet[2]);
+ enc_wrbreg(priv, ENC_MAADR5, priv->dev.d_mac.ether_addr_octet[1]);
+ enc_wrbreg(priv, ENC_MAADR6, priv->dev.d_mac.ether_addr_octet[0]);
+}
+
+/****************************************************************************
+ * Function: enc_reset
+ *
+ * Description:
+ * Stop, reset, re-initialize, and restart the ENC28J60. This is done
+ * initially, on ifup, and after a TX timeout.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_reset(FAR struct enc_driver_s *priv)
+{
+ uint8_t regval;
+
+ nlldbg("Reset\n");
+
+ /* Configure SPI for the ENC28J60 */
+
+ enc_configspi(priv->spi);
+
+ /* Reset the ENC28J60 */
+
+ enc_src(priv);
+
+ /* Initialize ECON1: Clear ECON1 */
+
+ enc_wrgreg(priv, ENC_ECON1, 0x00);
+
+ /* Initialize ECON2: Enable address auto increment and voltage
+ * regulator powersave.
+ */
+
+ enc_wrgreg(priv, ENC_ECON2, ECON2_AUTOINC | ECON2_VRPS);
+
+ /* Initialize receive buffer.
+ * First, set the receive buffer start address.
+ */
+
+ priv->nextpkt = PKTMEM_RX_START;
+ enc_wrbreg(priv, ENC_ERXSTL, PKTMEM_RX_START & 0xff);
+ enc_wrbreg(priv, ENC_ERXSTH, PKTMEM_RX_START >> 8);
+
+ /* Set the receive data pointer */
+
+ enc_wrbreg(priv, ENC_ERXRDPTL, PKTMEM_RX_START & 0xff);
+ enc_wrbreg(priv, ENC_ERXRDPTH, PKTMEM_RX_START >> 8);
+
+ /* Set the receive buffer end. */
+
+ enc_wrbreg(priv, ENC_ERXNDL, PKTMEM_RX_END & 0xff);
+ enc_wrbreg(priv, ENC_ERXNDH, PKTMEM_RX_END >> 8);
+
+ /* Set transmit buffer start. */
+
+ enc_wrbreg(priv, ENC_ETXSTL, PKTMEM_TX_START & 0xff);
+ enc_wrbreg(priv, ENC_ETXSTH, PKTMEM_TX_START >> 8);
+
+ /* Check if we are actually communicating with the ENC28J60. If its
+ * 0x00 or 0xff, then we are probably not communicating correctly
+ * via SPI.
+ */
+
+ regval = enc_rdbreg(priv, ENC_EREVID);
+ if (regval == 0x00 || regval == 0xff)
+ {
+ nlldbg("Bad Rev ID: %02x\n", regval);
+ return -ENODEV;
+ }
+
+ nllvdbg("Rev ID: %02x\n", regval);
+
+ /* Set filter mode: unicast OR broadcast AND crc valid */
+
+ enc_wrbreg(priv, ENC_ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN);
+
+ /* Enable MAC receive */
+
+ enc_wrbreg(priv, ENC_MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
+
+ /* Enable automatic padding and CRC operations */
+
+#ifdef CONFIG_ENC28J60_HALFDUPLEX
+ enc_wrbreg(priv, ENC_MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN);
+ enc_wrbreg(priv, ENC_MACON4, MACON4_DEFER); /* Defer transmission enable */
+
+ /* Set Non-Back-to-Back Inter-Packet Gap */
+
+ enc_wrbreg(priv, ENC_MAIPGL, 0x12);
+ enc_wrbreg(priv, ENC_MAIPGH, 0x0c);
+
+ /* Set Back-to-Back Inter-Packet Gap */
+
+ enc_wrbreg(priv, ENC_MABBIPG, 0x12);
+#else
+ /* Set filter mode: unicast OR broadcast AND crc valid AND Full Duplex */
+
+ enc_wrbreg(priv, ENC_MACON3,
+ MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX);
+
+ /* Set Non-Back-to-Back Inter-Packet Gap */
+
+ enc_wrbreg(priv, ENC_MAIPGL, 0x12);
+
+ /* Set ack-to-Back Inter-Packet Gap */
+
+ enc_wrbreg(priv, ENC_MABBIPG, 0x15);
+#endif
+
+ /* Set the maximum packet size which the controller will accept */
+
+ enc_wrbreg(priv, ENC_MAMXFLL, CONFIG_NET_BUFSIZE & 0xff);
+ enc_wrbreg(priv, ENC_MAMXFLH, CONFIG_NET_BUFSIZE >> 8);
+
+ /* Configure LEDs (No, just use the defaults for now) */
+ /* enc_wrphy(priv, ENC_PHLCON, ??); */
+
+ /* Setup up PHCON1 & 2 */
+
+#ifdef CONFIG_ENC28J60_HALFDUPLEX
+ enc_wrphy(priv, ENC_PHCON1, 0x00);
+ enc_wrphy(priv, ENC_PHCON2, PHCON2_HDLDIS);
+#else
+ enc_wrphy(priv, ENC_PHCON1, PHCON1_PDPXMD);
+ enc_wrphy(priv, ENC_PHCON2, 0x00);
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: enc_initialize
+ *
+ * Description:
+ * Initialize the Ethernet driver. The ENC28J60 device is assumed to be
+ * in the post-reset state upon entry to this function.
+ *
+ * Parameters:
+ * spi - A reference to the platform's SPI driver for the ENC28J60
+ * lower - The MCU-specific interrupt used to control low-level MCU
+ * functions (i.e., ENC28J60 GPIO interrupts).
+ * devno - If more than one ENC28J60 is supported, then this is the
+ * zero based number that identifies the ENC28J60;
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int enc_initialize(FAR struct spi_dev_s *spi,
+ FAR const struct enc_lower_s *lower, unsigned int devno)
+{
+ FAR struct enc_driver_s *priv;
+
+ DEBUGASSERT(devno < CONFIG_ENC28J60_NINTERFACES);
+ priv = &g_enc28j60[devno];
+
+ /* Initialize the driver structure */
+
+ memset(g_enc28j60, 0, CONFIG_ENC28J60_NINTERFACES*sizeof(struct enc_driver_s));
+ priv->dev.d_ifup = enc_ifup; /* I/F down callback */
+ priv->dev.d_ifdown = enc_ifdown; /* I/F up (new IP address) callback */
+ priv->dev.d_txavail = enc_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ priv->dev.d_addmac = enc_addmac; /* Add multicast MAC address */
+ priv->dev.d_rmmac = enc_rmmac; /* Remove multicast MAC address */
+#endif
+ priv->dev.d_private = priv; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ priv->txpoll = wd_create(); /* Create periodic poll timer */
+ priv->txtimeout = wd_create(); /* Create TX timeout timer */
+ priv->spi = spi; /* Save the SPI instance */
+ priv->lower = lower; /* Save the low-level MCU interface */
+
+ /* The interface should be in the down state. However, this function is called
+ * too early in initalization to perform the ENC28J60 reset in enc_ifdown. We
+ * are depending upon the fact that the application level logic will call enc_ifdown
+ * later to reset the ENC28J60. NOTE: The MAC address will not be set up until
+ * enc_ifup() is called. That gives the app time to set the MAC address before
+ * bringing the interface up.
+ */
+
+ priv->ifstate = ENCSTATE_UNIT;
+
+ /* Attach the interrupt to the driver (but don't enable it yet) */
+
+ if (lower->attach(lower, enc_interrupt))
+ {
+ /* We could not attach the ISR to the interrupt */
+
+ return -EAGAIN;
+ }
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ return netdev_register(&priv->dev);
+}
+
+/****************************************************************************
+ * Function: enc_stats
+ *
+ * Description:
+ * Return accumulated ENC28J60 statistics. Statistics are cleared after
+ * being returned.
+ *
+ * Parameters:
+ * devno - If more than one ENC28J60 is supported, then this is the
+ * zero based number that identifies the ENC28J60;
+ * stats - The user-provided location to return the statistics.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ENC28J60_STATS
+int enc_stats(unsigned int devno, struct enc_stats_s *stats)
+{
+ FAR struct enc_driver_s *priv ;
+ irqstate_t flags;
+
+ DEBUGASSERT(devno < CONFIG_ENC28J60_NINTERFACES);
+ priv = &g_enc28j60[devno];
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+ memcpy(stats, &priv->stats, sizeof(struct enc_stats_s));
+ memset(&priv->stats, 0, sizeof(struct enc_stats_s));
+ irqrestore(flags);
+ return OK;
+}
+#endif
+#endif /* CONFIG_NET && CONFIG_ENC28J60_NET */
+
diff --git a/nuttx/drivers/net/enc28j60.h b/nuttx/drivers/net/enc28j60.h
new file mode 100644
index 000000000..3c787c533
--- /dev/null
+++ b/nuttx/drivers/net/enc28j60.h
@@ -0,0 +1,478 @@
+/****************************************************************************
+ * drivers/net/enc28j60.h
+ *
+ * Copyright (C) 2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * - ENC28J60 Data Sheet, Stand-Alone Ethernet Controller with SPI Interface,
+ * DS39662C, 2008 Microchip Technology Inc.
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_NET_ENC28J60_H
+#define __DRIVERS_NET_ENC28J60_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* ENC28J60 Commands ********************************************************/
+/* A total of seven instructions are implemented on the ENC28J60. Where:
+ *
+ * aaaaaa is the 5-bit address of a control register, and
+ * dddddddd is one or more bytes of data that may accompany the command.
+ */
+
+#define ENC_RCR (0x00) /* Read Control Register
+ * 000 | aaaaa | (Register value returned)) */
+#define ENC_RBM (0x3a) /* Read Buffer Memory
+ * 001 | 11010 | (Read buffer data follows) */
+#define ENC_WCR (0x40) /* Write Control Register
+ * 010 | aaaaa | dddddddd */
+#define ENC_WBM (0x7a) /* Write Buffer Memory
+ * 011 | 11010 | (Write buffer data follows) */
+#define ENC_BFS (0x80) /* Bit Field Set
+ * 100 | aaaaa | dddddddd */
+#define ENC_BFC (0xa0) /* Bit Field Clear
+ * 101 | aaaaa | dddddddd */
+#define ENC_SRC (0xff) /* System Reset
+ * 111 | 11111 | (No data) */
+
+/* Global Control Registers *************************************************/
+/* Control registers are accessed with the RCR, RBM, WCR, BFS, and BFC
+ * commands. The following identifies all ENC28J60 control registers. The
+ * control register memory is partitioned into four banks, selectable by the
+ * bank select bits, BSEL1:BSEL0, in the ECON1 register.
+ *
+ * The last five locations (0x1b to 0x1f) of all banks point to a common set
+ * of registers: EIE, EIR, ESTAT, ECON2 and ECON1. These are key registers
+ * usedin controlling and monitoring the operation of the device. Their
+ * common mapping allows easy access without switching the bank.
+ *
+ * Control registers for the ENC28J60 are generically grouped as ETH, MAC and
+ * MII registers. Register names starting with E belong to the ETH group.
+ * Similarly, registers names starting with MA belong to the MAC group and
+ * registers prefixed with MI belong to the MII group.
+ */
+
+#define ENC_EIE (0x1b) /* Ethernet Interrupt Enable Register */
+#define ENC_EIR (0x1c) /* Ethernet Interupt Request Register */
+#define ENC_ESTAT (0x1d) /* Ethernet Status Register */
+#define ENC_ECON2 (0x1e) /* Ethernet Control 2 Register */
+#define ENC_ECON1 (0x1f) /* Ethernet Control 1 Register */
+
+/* Ethernet Interrupt Enable Register Bit Definitions */
+
+#define EIE_RXERIE (1 << 0) /* Bit 0: Receive Error Interrupt Enable */
+#define EIE_TXERIE (1 << 1) /* Bit 1: Transmit Error Interrupt Enable */
+ /* Bit 2: Reserved */
+#define EIE_TXIE (1 << 3) /* Bit 3: Transmit Enable */
+#define EIE_LINKIE (1 << 4) /* Bit 4: Link Status Change Interrupt Enable */
+#define EIE_DMAIE (1 << 5) /* Bit 5: DMA Interrupt Enable */
+#define EIE_PKTIE (1 << 6) /* Bit 6: Receive Packet Pending Interrupt Enable */
+#define EIE_INTIE (1 << 7) /* Bit 7: Global INT Interrupt Enable */
+
+/* Ethernet Interrupt Request Register Bit Definitions */
+
+#define EIR_RXERIF (1 << 0) /* Bit 0: Receive Error Interrupt */
+#define EIR_TXERIF (1 << 1) /* Bit 1: Transmit Error Interrupt */
+ /* Bit 2: Reserved */
+#define EIR_TXIF (1 << 3) /* Bit 3: Transmit Interrupt */
+#define EIR_LINKIF (1 << 4) /* Bit 4: Link Change Interrupt */
+#define EIR_DMAIF (1 << 5) /* Bit 5: DMA Interrupt */
+#define EIR_PKTIF (1 << 6) /* Bit 6: Receive Packet Pending Interrupt */
+ /* Bit 7: Reserved */
+#define EIR_ALLINTS (0x7b) /* All interrupts */
+
+/* Ethernet Status Register Bit Definitions */
+
+#define ESTAT_CLKRDY (1 << 0) /* Bit 0: Clock Ready */
+#define ESTAT_TXABRT (1 << 1) /* Bit 1: Transmit Abort Error */
+#define ESTAT_RXBUSY (1 << 2) /* Bit 2: Receive Busy */
+ /* Bit 3: Reserved */
+#define ESTAT_LATECOL (1 << 4) /* Bit 4: Late Collision Error */
+ /* Bit 5: Reserved */
+#define ESTAT_BUFER (1 << 6) /* Bit 6: Ethernet Buffer Error Status */
+#define ESTAT_INT (1 << 7) /* Bit 7: INT Interrupt */
+
+/* Ethernet Control 1 Register Bit Definitions */
+
+#define ECON1_BSEL_SHIFT (0) /* Bits 0-1: Bank select */
+#define ECON1_BSEL_MASK (3 << ECON1_BSEL_SHIFT)
+# define ECON1_BSEL_BANK0 (0 << 0) /* Bank 0 */
+# define ECON1_BSEL_BANK1 (1 << 1) /* Bank 1 */
+# define ECON1_BSEL_BANK2 (2 << 0) /* Bank 2 */
+# define ECON1_BSEL_BANK3 (3 << 0) /* Bank 3 */
+#define ECON1_RXEN (1 << 2) /* Bit 2: Receive Enable */
+#define ECON1_TXRTS (1 << 3) /* Bit 3: Transmit Request to Send */
+#define ECON1_CSUMEN (1 << 4) /* Bit 4: DMA Checksum Enable */
+#define ECON1_DMAST (1 << 5) /* Bit 5: DMA Start and Busy Status */
+#define ECON1_RXRST (1 << 6) /* Bit 6: Receive Logic Reset */
+#define ECON1_TXRST (1 << 7) /* Bit 7: Transmit Logic Reset */
+
+/* Ethernet Control 2 Register */
+ /* Bits 0-2: Reserved */
+#define ECON2_VRPS (1 << 3) /* Bit 3: Voltage Regulator Power Save Enable */
+ /* Bit 4: Reserved */
+#define ECON2_PWRSV (1 << 5) /* Bit 5: Power Save Enable */
+#define ECON2_PKTDEC (1 << 6) /* Bit 6: Packet Decrement */
+#define ECON2_AUTOINC (1 << 7) /* Bit 7: Automatic Buffer Pointer Increment Enable */
+
+/* Banked Control Registers *************************************************/
+/* The remaining control registers are identified with a a 5 bit address and
+ * a bank selection. We pack the bank number and an indication if this is
+ * a MAC/PHY register access together with the control register address
+ * together to keep the design simpler.
+ */
+
+#define ENC_ADDR_SHIFT (0) /* Bits 0-4: Register address */
+#define ENC_ADDR_MASK (0x1f << ENC_ADDR_SHIFT)
+#define ENC_BANK_SHIFT (5) /* Bits 5-6: Bank number */
+#define ENC_BANK_MASK (3 << ENC_BSEL_SHIFT)
+# define ENC_BANK0 (0 << ENC_BSEL_SHIFT)
+# define ENC_BANK1 (1 << ENC_BSEL_SHIFT)
+# define ENC_BANK2 (2 << ENC_BSEL_SHIFT)
+# define ENC_BANK3 (3 << ENC_BSEL_SHIFT)
+#define ENC_PHYMAC_SHIFT (7) /* Bit 7: This is a PHY/MAC command */
+#define ENC_PHYMAC (1 << ENC_PHYMAC_SHIFT)
+
+#define REGADDR(a,b,m) ((m) << ENC_PHYMAC_SHIFT | (b) << ENC_BANK_SHIFT | (a))
+#define GETADDR(a) ((a) & ENC_ADDR_MASK)
+#define GETBANK(a) (((a) >> ENC_BANK_SHIFT) & 3)
+#define ISPHYMAC(a) (((a) & ENC_PHYMAC) != 0)
+
+/* Bank 0 Control Register Addresses */
+
+#define ENC_ERDPTL REGADDR(0x00, 0, 0) /* Read Pointer Low Byte (ERDPT<7:0> */
+#define ENC_ERDPTH REGADDR(0x01, 0, 0) /* Read Pointer High Byte (ERDPT<12:8>) */
+#define ENC_EWRPTL REGADDR(0x02, 0, 0) /* Write Pointer Low Byte (EWRPT<7:0>) */
+#define ENC_EWRPTH REGADDR(0x03, 0, 0) /* Write Pointer High Byte (EWRPT<12:8>) */
+#define ENC_ETXSTL REGADDR(0x04, 0, 0) /* TX Start Low Byte (ETXST<7:0>) */
+#define ENC_ETXSTH REGADDR(0x05, 0, 0) /* TX Start High Byte (ETXST<12:8>) */
+#define ENC_ETXNDL REGADDR(0x06, 0, 0) /* TX End Low Byte (ETXND<7:0>) */
+#define ENC_ETXNDH REGADDR(0x07, 0, 0) /* TX End High Byte (ETXND<12:8>) */
+#define ENC_ERXSTL REGADDR(0x08, 0, 0) /* RX Start Low Byte (ERXST<7:0>) */
+#define ENC_ERXSTH REGADDR(0x09, 0, 0) /* RX Start High Byte (ERXST<12:8>) */
+#define ENC_ERXNDL REGADDR(0x0a, 0, 0) /* RX End Low Byte (ERXND<7:0>) */
+#define ENC_ERXNDH REGADDR(0x0b, 0, 0) /* RX End High Byte (ERXND<12:8>) */
+#define ENC_ERXRDPTL REGADDR(0x0c, 0, 0) /* RX RD Pointer Low Byte (ERXRDPT<7:0>) */
+#define ENC_ERXRDPTH REGADDR(0x0d, 0, 0) /* RX RD Pointer High Byte (ERXRDPT<12:8>) */
+#define ENC_ERXWRPTL REGADDR(0x0e, 0, 0) /* RX WR Pointer Low Byte (ERXWRPT<7:0>) */
+#define ENC_ERXWRPTH REGADDR(0x0f, 0, 0) /* RX WR Pointer High Byte (ERXWRPT<12:8>) */
+#define ENC_EDMASTL REGADDR(0x10, 0, 0) /* DMA Start Low Byte (EDMAST<7:0>) */
+#define ENC_EDMASTH REGADDR(0x11, 0, 0) /* DMA Start High Byte (EDMAST<12:8>) */
+#define ENC_EDMANDL REGADDR(0x12, 0, 0) /* DMA End Low Byte (EDMAND<7:0>) */
+#define ENC_EDMANDH REGADDR(0x13, 0, 0) /* DMA End High Byte (EDMAND<12:8>) */
+#define ENC_EDMADSTL REGADDR(0x14, 0, 0) /* DMA Destination Low Byte (EDMADST<7:0>) */
+#define ENC_EDMADSTH REGADDR(0x15, 0, 0) /* DMA Destination High Byte (EDMADST<12:8>) */
+#define ENC_EDMACSL REGADDR(0x16, 0, 0) /* DMA Checksum Low Byte (EDMACS<7:0>) */
+#define ENC_EDMACSH REGADDR(0x17, 0, 0) /* DMA Checksum High Byte (EDMACS<15:8>) */
+ /* 0x18-0x1a: Reserved */
+ /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */
+/* Bank 1 Control Register Addresses */
+
+#define ENC_EHT0 REGADDR(0x00, 1, 0) /* Hash Table Byte 0 (EHT<7:0>) */
+#define ENC_EHT1 REGADDR(0x01, 1, 0) /* Hash Table Byte 1 (EHT<15:8>) */
+#define ENC_EHT2 REGADDR(0x02, 1, 0) /* Hash Table Byte 2 (EHT<23:16>) */
+#define ENC_EHT3 REGADDR(0x03, 1, 0) /* Hash Table Byte 3 (EHT<31:24>) */
+#define ENC_EHT4 REGADDR(0x04, 1, 0) /* Hash Table Byte 4 (EHT<39:32>) */
+#define ENC_EHT5 REGADDR(0x05, 1, 0) /* Hash Table Byte 5 (EHT<47:40>) */
+#define ENC_EHT6 REGADDR(0x06, 1, 0) /* Hash Table Byte 6 (EHT<55:48>) */
+#define ENC_EHT7 REGADDR(0x07, 1, 0) /* Hash Table Byte 7 (EHT<63:56>) */
+#define ENC_EPMM0 REGADDR(0x08, 1, 0) /* Pattern Match Mask Byte 0 (EPMM<7:0>) */
+#define ENC_EPMM1 REGADDR(0x09, 1, 0) /* Pattern Match Mask Byte 1 (EPMM<15:8>) */
+#define ENC_EPMM2 REGADDR(0x0a, 1, 0) /* Pattern Match Mask Byte 2 (EPMM<23:16>) */
+#define ENC_EPMM3 REGADDR(0x0b, 1, 0) /* Pattern Match Mask Byte 3 (EPMM<31:24>) */
+#define ENC_EPMM4 REGADDR(0x0c, 1, 0) /* Pattern Match Mask Byte 4 (EPMM<39:32>) */
+#define ENC_EPMM5 REGADDR(0x0d, 1, 0) /* Pattern Match Mask Byte 5 (EPMM<47:40>) */
+#define ENC_EPMM6 REGADDR(0x0e, 1, 0) /* Pattern Match Mask Byte 6 (EPMM<55:48>) */
+#define ENC_EPMM7 REGADDR(0x0f, 1, 0) /* Pattern Match Mask Byte 7 (EPMM<63:56>) */
+#define ENC_EPMCSL REGADDR(0x10, 1, 0) /* Pattern Match Checksum Low Byte (EPMCS<7:0>) */
+#define ENC_EPMCSH REGADDR(0x11, 1, 0) /* Pattern Match Checksum High Byte (EPMCS<15:0>) */
+ /* 0x12-0x13: Reserved */
+#define ENC_EPMOL REGADDR(0x14, 1, 0) /* Pattern Match Offset Low Byte (EPMO<7:0>) */
+#define ENC_EPMOH REGADDR(0x15, 1, 0) /* Pattern Match Offset High Byte (EPMO<12:8>) */
+ /* 0x16-0x17: Reserved */
+#define ENC_ERXFCON REGADDR(0x18, 1, 0) /* Receive Filter Configuration */
+#define ENC_EPKTCNT REGADDR(0x19, 1, 0) /* Ethernet Packet Count */
+ /* 0x1a: Reserved */
+ /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */
+
+/* Receive Filter Configuration Bit Definitions */
+
+#define ERXFCON_BCEN (1 << 0) /* Bit 0: Broadcast Filter Enable */
+#define ERXFCON_MCEN (1 << 1) /* Bit 1: Multicast Filter Enable */
+#define ERXFCON_HTEN (1 << 2) /* Bit 2: Hash Table Filter Enable */
+#define ERXFCON_MPEN (1 << 3) /* Bit 3: Magic Packet Filter Enable */
+#define ERXFCON_PMEN (1 << 4) /* Bit 4: Pattern Match Filter Enable */
+#define ERXFCON_CRCEN (1 << 5) /* Bit 5: Post-Filter CRC Check Enable */
+#define ERXFCON_ANDOR (1 << 6) /* Bit 6: AND/OR Filter Select */
+#define ERXFCON_UCEN (1 << 7) /* Bit 7: Unicast Filter Enable */
+
+/* Bank 2 Control Register Addresses */
+
+#define ENC_MACON1 REGADDR(0x00, 2, 1) /* MAC Control 1 */
+ /* 0x01: Reserved */
+#define ENC_MACON3 REGADDR(0x02, 2, 1) /* MAC Control 3 */
+#define ENC_MACON4 REGADDR(0x03, 2, 1) /* MAC Control 4 */
+#define ENC_MABBIPG REGADDR(0x04, 2, 1) /* Back-to-Back Inter-Packet Gap (BBIPG<6:0>) */
+ /* 0x05: Reserved */
+#define ENC_MAIPGL REGADDR(0x06, 2, 1) /* Non-Back-to-Back Inter-Packet Gap Low Byte (MAIPGL<6:0>) */
+#define ENC_MAIPGH REGADDR(0x07, 2, 1) /* Non-Back-to-Back Inter-Packet Gap High Byte (MAIPGH<6:0>) */
+#define ENC_MACLCON1 REGADDR(0x08, 2, 1) /* MAC Collision Control 1 */
+#define ENC_MACLCON2 REGADDR(0x09, 2, 1) /* MAC Collision Control 2 */
+#define ENC_MAMXFLL REGADDR(0x0a, 2, 1) /* Maximum Frame Length Low Byte (MAMXFL<7:0>) */
+#define ENC_MAMXFLH REGADDR(0x0b, 2, 1) /* Maximum Frame Length High Byte (MAMXFL<15:8>) */
+ /* 0x0c-0x11: Reserved */
+#define ENC_MICMD REGADDR(0x12, 2, 1) /* MII Command Register */
+ /* 0x13: Reserved */
+#define ENC_MIREGADR REGADDR(0x14, 2, 1) /* MII Register Address */
+ /* 0x15: Reserved */
+#define ENC_MIWRL REGADDR(0x16, 2, 1) /* MII Write Data Low Byte (MIWR<7:0>) */
+#define ENC_MIWRH REGADDR(0x17, 2, 1) /* MII Write Data High Byte (MIWR<15:8>) */
+#define ENC_MIRDL REGADDR(0x18, 2, 1) /* MII Read Data Low Byte (MIRD<7:0>) */
+#define ENC_MIRDH REGADDR(0x19, 2, 1) /* MII Read Data High Byte(MIRD<15:8>) */
+ /* 0x1a: Reserved */
+ /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */
+
+/* MAC Control 1 Register Bit Definitions */
+
+#define MACON1_MARXEN (1 << 0) /* Bit 0: MAC Receive Enable */
+#define MACON1_PASSALL (1 << 1) /* Bit 1: Pass All Received Frames Enable */
+#define MACON1_RXPAUS (1 << 2) /* Bit 2: Pause Control Frame Reception Enable */
+#define MACON1_TXPAUS (1 << 3) /* Bit 3: Pause Control Frame Transmission Enable */
+ /* Bits 4-7: Unimplemented or reserved */
+
+/* MAC Control 1 Register Bit Definitions */
+
+#define MACON3_FULDPX (1 << 0) /* Bit 0: MAC Full-Duplex Enable */
+#define MACON3_FRMLNEN (1 << 1) /* Bit 1: Frame Length Checking Enable */
+#define MACON3_HFRMLEN (1 << 2) /* Bit 2: Huge Frame Enable */
+#define MACON3_PHDRLEN (1 << 3) /* Bit 3: Proprietary Header Enable */
+#define MACON3_TXCRCEN (1 << 4) /* Bit 4: Transmit CRC Enable */
+#define MACON3_PADCFG0 (1 << 5) /* Bit 5: Automatic Pad and CRC Configuration */
+#define MACON3_PADCFG1 (1 << 6) /* Bit 6: " " " " " " " " " " */
+#define MACON3_PADCFG2 (1 << 7) /* Bit 7: " " " " " " " " " " */
+
+/* MAC Control 1 Register Bit Definitions */
+
+#define MACON4_NOBKOFF (1 << 4) /* Bit 4: No Backoff Enable */
+#define MACON4_BPEN (1 << 5) /* Bit 5: No Backoff During Backpressure Enable */
+#define MACON4_DEFER (1 << 6) /* Bit 6: Defer Transmission Enable bit */
+
+/* MII Command Register Bit Definitions */
+
+#define MICMD_MIIRD (1 << 0) /* Bit 0: MII Read Enable */
+#define MICMD_MIISCAN (1 << 1) /* Bit 1: MII Scan Enable */
+
+/* Bank 3 Control Register Addresses */
+
+#define ENC_MAADR5 REGADDR(0x00, 3, 1) /* MAC Address Byte 5 (MAADR<15:8>) */
+#define ENC_MAADR6 REGADDR(0x01, 3, 1) /* MAC Address Byte 6 (MAADR<7:0>) */
+#define ENC_MAADR3 REGADDR(0x02, 3, 1) /* MAC Address Byte 3 (MAADR<31:24>), OUI Byte 3 */
+#define ENC_MAADR4 REGADDR(0x03, 3, 1) /* MAC Address Byte 4 (MAADR<23:16>) */
+#define ENC_MAADR1 REGADDR(0x04, 3, 1) /* MAC Address Byte 1 (MAADR<47:40>), OUI Byte 1 */
+#define ENC_MAADR2 REGADDR(0x05, 3, 1) /* MAC Address Byte 2 (MAADR<39:32>), OUI Byte */
+#define ENC_EBSTSD REGADDR(0x06, 3, 0) /* Built-in Self-Test Fill Seed (EBSTSD<7:0>) */
+#define ENC_EBSTCON REGADDR(0x07, 3, 0) /* Built-in Self-Test Control */
+#define ENC_EBSTCSL REGADDR(0x08, 3, 0) /* Built-in Self-Test Checksum Low Byte (EBSTCS<7:0>) */
+#define ENC_EBSTCSH REGADDR(0x09, 3, 0) /* Built-in Self-Test Checksum High Byte (EBSTCS<15:8>) */
+#define ENC_MISTAT REGADDR(0x0a, 3, 1) /* MII Status Register */
+ /* 0x0b-0x11: Reserved */
+#define ENC_EREVID REGADDR(0x12, 3, 0) /* Ethernet Revision ID */
+ /* 0x13-0x14: Reserved */
+#define ENC_ECOCON REGADDR(0x15, 3, 0) /* Clock Output Control */
+ /* 0x16: Reserved */
+#define ENC_EFLOCON REGADDR(0x17, 3, 0) /* Ethernet Flow Control */
+#define ENC_EPAUSL REGADDR(0x18, 3, 0) /* Pause Timer Value Low Byte (EPAUS<7:0>) */
+#define ENC_EPAUSH REGADDR(0x19, 3, 0) /* Pause Timer Value High Byte (EPAUS<15:8>) */
+ /* 0x1a: Reserved */
+ /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */
+
+/* Built-in Self-Test Control Register Bit Definitions */
+
+#define EBSTCON_BISTST (1 << 0) /* Bit 0: Built-in Self-Test Start/Busy */
+#define EBSTCON_TME (1 << 1) /* Bit 1: Test Mode Enable */
+#define EBSTCON_TMSEL0 (1 << 2) /* Bit 2: Test Mode Select */
+#define EBSTCON_TMSEL1 (1 << 3) /* Bit 3: " " " " " " */
+#define EBSTCON_PSEL (1 << 4) /* Bit 4: Port Select */
+#define EBSTCON_PSV0 (1 << 5) /* Bit 5: Pattern Shift Value */
+#define EBSTCON_PSV1 (1 << 6) /* Bit 6: " " " " " */
+#define EBSTCON_PSV2 (1 << 7) /* Bit 7: " " " " " */
+
+/* MII Status Register Register Bit Definitions */
+
+#define MISTAT_BUSY (1 << 0) /* Bit 0: MII Management Busy */
+#define MISTAT_SCAN (1 << 1) /* Bit 1: MII Management Scan Operation */
+#define MISTAT_NVALID (1 << 2) /* Bit 2: MII Management Read Data Not Valid */
+ /* Bits 3-7: Reserved or unimplemented */
+
+/* Ethernet Flow Control Register Bit Definitions */
+
+#define EFLOCON_FCEN0 (1 << 0) /* Bit 0: Flow Control Enable */
+#define EFLOCON_FCEN1 (1 << 1) /* Bit 1: " " " " " " */
+#define EFLOCON_FULDPXS (1 << 2) /* Bit 2: Read-Only MAC Full-Duplex Shadow */
+ /* Bits 3-7: Reserved or unimplemented */
+
+/* PHY Registers ************************************************************/
+
+#define ENC_PHCON1 (0x00) /* PHY Control Register 1 */
+#define ENC_PHSTAT1 (0x01) /* PHY Status 1 */
+#define ENC_PHID1 (0x02) /* PHY ID Register 1 */
+#define ENC_PHID2 (0x03) /* PHY ID Register 2 */
+#define ENC_PHCON2 (0x10) /* PHY Control Register 2 */
+#define ENC_PHSTAT2 (0x11) /* PHY Status 2 */
+#define ENC_PHIE (0x12) /* PHY Interrupt Enable Register */
+#define ENC_PHIR (0x13) /* PHY Interrupt Request Register */
+#define ENC_PHLCON (0x14)
+
+/* PHY Control Register 1 Register Bit Definitions */
+
+#define PHCON1_PDPXMD (1 << 8) /* Bit 8: PHY Power-Down */
+#define PHCON1_PPWRSV (1 << 11) /* Bit 11: PHY Power-Down */
+#define PHCON1_PLOOPBK (1 << 14) /* Bit 14: PHY Loopback */
+#define PHCON1_PRST (1 << 15) /* Bit 15: PHY Software Reset */
+
+/* HY Status 1 Register Bit Definitions */
+
+#define PHSTAT1_JBSTAT (1 << 1) /* Bit 1: PHY Latching Jabber Status */
+#define PHSTAT1_LLSTAT (1 << 2) /* Bit 2: PHY Latching Link Status */
+#define PHSTAT1_PHDPX (1 << 11) /* Bit 11: PHY Half-Duplex Capable */
+#define PHSTAT1_PFDPX (1 << 12) /* Bit 12: PHY Full-Duplex Capable */
+
+/* PHY Control Register 2 Register Bit Definitions */
+
+#define PHCON2_HDLDIS (1 << 8) /* Bit 8: PHY Half-Duplex Loopback Disable */
+#define PHCON2_JABBER (1 << 10) /* Bit 10: Jabber Correction Disable */
+#define PHCON2_TXDIS (1 << 13) /* Bit 13: Twisted-Pair Transmitter Disable */
+#define PHCON2_FRCLINK (1 << 14) /* Bit 14: PHY Force Linkup */
+
+/* PHY Status 2 Register Bit Definitions */
+
+#define PHSTAT2_PLRITY (1 << 5) /* Bit 5: Polarity Status */
+#define PHSTAT2_DPXSTAT (1 << 9) /* Bit 9: PHY Duplex Status */
+#define PHSTAT2_LSTAT (1 << 10) /* Bit 10: PHY Link Status */
+#define PHSTAT2_COLSTAT (1 << 11) /* Bit 11: PHY Collision Status */
+#define PHSTAT2_RXSTAT (1 << 12) /* Bit 12: PHY Receive Status */
+#define PHSTAT2_TXSTAT (1 << 13) /* Bit 13: PHY Transmit Status */
+
+/* PHY Interrupt Enable Register Bit Definitions */
+
+#define PHIE_PGEIE (1 << 1) /* Bit 1: PHY Global Interrupt Enable */
+#define PHIE_PLNKIE (1 << 4) /* Bit 4: PHY Link Change Interrupt Enable */
+
+/* PHIR Regiser Bit Definitions */
+
+#define PHIR_PGIF (1 << 2) /* Bit 2: PHY Global Interrupt */
+#define PHIR_PLNKIF (1 << 4) /* Bit 4: PHY Link Change Interrupt */
+
+/* PHLCON Regiser Bit Definitions */
+
+ /* Bit 0: Reserved */
+#define PHLCON_STRCH (1 << 1) /* Bit 1: LED Pulse Stretching Enable */
+#define PHLCON_LFRQ0 (1 << 2) /* Bit 2: LED Pulse Stretch Time Configuration */
+#define PHLCON_LFRQ1 (1 << 3) /* Bit 3: " " " " " " " " " */
+#define PHLCON_LBCFG0 (1 << 4) /* Bit 4: LEDB Configuration */
+#define PHLCON_LBCFG1 (1 << 5) /* Bit 5: " " " " */
+#define PHLCON_LBCFG2 (1 << 6) /* Bit 6: " " " " */
+#define PHLCON_LBCFG3 (1 << 7) /* Bit 7: " " " " */
+#define PHLCON_LACFG0 (1 << 8) /* Bit 8: LEDA Configuration */
+#define PHLCON_LACFG1 (1 << 9) /* Bit 9: " " " " */
+#define PHLCON_LACFG2 (1 << 10) /* Bit 10: " " " " */
+#define PHLCON_LACFG3 (1 << 11) /* Bit 11: " " " " */
+
+/* Packet Memory ************************************************************/
+
+/* 8-Kbyte Transmit/Receive Packet Dual Port SRAM */
+
+#define PKTMEM_START 0x0000
+#define PKTMEM_END 0x1fff
+
+/* Ethernet frames are between 64 and 1518 bytes long */
+
+#define MIN_FRAMELEN 64
+#define MAX_FRAMELEN 1518
+
+/* Packet Control Bits Definitions ******************************************/
+
+#define PKTCTRL_POVERRIDE (1 << 0) /* Bit 0: Per Packet Override */
+#define PKTCTRL_PCRCEN (1 << 1) /* Bit 1: Per Packet CRC Enable */
+#define PKTCTRL_PPADEN (1 << 2) /* Bit 2: Per Packet Padding Enable */
+#define PKTCTRL_PHUGEEN (1 << 3) /* Bit 3: Per Packet Huge Frame Enable */
+
+/* RX Status Bit Definitions ************************************************/
+
+#define RXSTAT_LDEVENT (1 << 0) /* Bit 0: Long event or pack dropped */
+ /* Bit 1: Reserved */
+#define RXSTAT_CEPS (1 << 2) /* Bit 2: Carrier event previously seen */
+ /* Bit 3: Reserved */
+#define RXSTAT_CRCERROR (1 << 4) /* Bit 4: Frame CRC field bad */
+#define RXSTAT_LENERROR (1 << 5) /* Bit 5: Packet length != data length */
+#define RXSTAT_LENRANGE (1 << 6) /* Bit 6: Type/length field > 1500 bytes */
+#define RXSTAT_OK (1 << 7) /* Bit 7: Packet with valid CRC and no symbol errors */
+#define RXSTAT_MCAST (1 << 8) /* Bit 8: Packet with multicast address */
+#define RXSTAT_BCAST (1 << 9) /* Bit 9: Packet with broadcast address */
+#define RXSTAT_DRIBBLE (1 << 10) /* Bit 10: Additional bits received after packet */
+#define RXSTAT_CTRLFRAME (1 << 11) /* Bit 11: Control frame with valid type/length */
+#define RXSTAT_PAUSE (1 << 12) /* Bit 12: Control frame with pause frame opcde */
+#define RXSTAT_UNKOPCODE (1 << 13) /* Bit 13: Control frame with unknown opcode */
+#define RXSTAT_VLANTYPE (1 << 14) /* Bit 14: Current frame is a VLAN tagged frame */
+ /* Bit 15: Zero */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRIVERS_NET_ENC28J60_H */
diff --git a/nuttx/drivers/net/skeleton.c b/nuttx/drivers/net/skeleton.c
new file mode 100644
index 000000000..00ebea35f
--- /dev/null
+++ b/nuttx/drivers/net/skeleton.c
@@ -0,0 +1,692 @@
+/****************************************************************************
+ * drivers/net/skeleton.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#if defined(CONFIG_NET) && defined(CONFIG_NET_skeleton)
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* CONFIG_skeleton_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_skeleton_NINTERFACES
+# define CONFIG_skeleton_NINTERFACES 1
+#endif
+
+/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define skeleton_WDDELAY (1*CLK_TCK)
+#define skeleton_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define skeleton_TXTIMEOUT (60*CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)skel->sk_dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The skel_driver_s encapsulates all state information for a single hardware
+ * interface
+ */
+
+struct skel_driver_s
+{
+ bool sk_bifup; /* true:ifup false:ifdown */
+ WDOG_ID sk_txpoll; /* TX poll timer */
+ WDOG_ID sk_txtimeout; /* TX timeout timer */
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s sk_dev; /* Interface understood by uIP */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct skel_driver_s g_skel[CONFIG_skeleton_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static int skel_transmit(FAR struct skel_driver_s *skel);
+static int skel_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void skel_receive(FAR struct skel_driver_s *skel);
+static void skel_txdone(FAR struct skel_driver_s *skel);
+static int skel_interrupt(int irq, FAR void *context);
+
+/* Watchdog timer expirations */
+
+static void skel_polltimer(int argc, uint32_t arg, ...);
+static void skel_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int skel_ifup(struct uip_driver_s *dev);
+static int skel_ifdown(struct uip_driver_s *dev);
+static int skel_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int skel_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int skel_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: skel_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * skel - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int skel_transmit(FAR struct skel_driver_s *skel)
+{
+ /* Verify that the hardware is ready to send another packet. If we get
+ * here, then we are committed to sending a packet; Higher level logic
+ * must have assured that there is no transmission in progress.
+ */
+
+ /* Increment statistics */
+
+ /* Send the packet: address=skel->sk_dev.d_buf, length=skel->sk_dev.d_len */
+
+ /* Enable Tx interrupts */
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ (void)wd_start(skel->sk_txtimeout, skeleton_TXTIMEOUT, skel_txtimeout, 1, (uint32_t)skel);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: skel_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timesout and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int skel_uiptxpoll(struct uip_driver_s *dev)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+
+ /* If the polling resulted in data that should be sent out on the network,
+ * the field d_len is set to a value > 0.
+ */
+
+ if (skel->sk_dev.d_len > 0)
+ {
+ uip_arp_out(&skel->sk_dev);
+ skel_transmit(skel);
+
+ /* Check if there is room in the device to hold another packet. If not,
+ * return a non-zero value to terminate the poll.
+ */
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: skel_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * skel - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void skel_receive(FAR struct skel_driver_s *skel)
+{
+ do
+ {
+ /* Check for errors and update statistics */
+
+ /* Check if the packet is a valid size for the uIP buffer configuration */
+
+ /* Copy the data data from the hardware to skel->sk_dev.d_buf. Set
+ * amount of data in skel->sk_dev.d_len
+ */
+
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ uip_arp_ipin(&skel->sk_dev);
+ uip_input(&skel->sk_dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (skel->sk_dev.d_len > 0)
+ {
+ uip_arp_out(&skel->sk_dev);
+ skel_transmit(skel);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP))
+ {
+ uip_arp_arpin(&skel->sk_dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (skel->sk_dev.d_len > 0)
+ {
+ skel_transmit(skel);
+ }
+ }
+ }
+ while (); /* While there are more packets to be processed */
+}
+
+/****************************************************************************
+ * Function: skel_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Parameters:
+ * skel - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void skel_txdone(FAR struct skel_driver_s *skel)
+{
+ /* Check for errors and update statistics */
+
+ /* If no further xmits are pending, then cancel the TX timeout and
+ * disable further Tx interrupts.
+ */
+
+ wd_cancel(skel->sk_txtimeout);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&skel->sk_dev, skel_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: skel_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int skel_interrupt(int irq, FAR void *context)
+{
+ register FAR struct skel_driver_s *skel = &g_skel[0];
+
+ /* Get and clear interrupt status bits */
+
+ /* Handle interrupts according to status bit settings */
+
+ /* Check if we received an incoming packet, if so, call skel_receive() */
+
+ skel_receive(skel);
+
+ /* Check if a packet transmission just completed. If so, call skel_txdone.
+ * This may disable further Tx interrupts if there are no pending
+ * tansmissions.
+ */
+
+ skel_txdone(skel);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: skel_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void skel_txtimeout(int argc, uint32_t arg, ...)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)arg;
+
+ /* Increment statistics and dump debug info */
+
+ /* Then reset the hardware */
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&skel->sk_dev, skel_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: skel_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void skel_polltimer(int argc, uint32_t arg, ...)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)arg;
+
+ /* Check if there is room in the send another TX packet. We cannot perform
+ * the TX poll if he are unable to accept another packet for transmission.
+ */
+
+ /* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm..
+ * might be bug here. Does this mean if there is a transmit in progress,
+ * we will missing TCP time state updates?
+ */
+
+ (void)uip_timer(&skel->sk_dev, skel_uiptxpoll, skeleton_POLLHSEC);
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(skel->sk_txpoll, skeleton_WDDELAY, skel_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: skel_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int skel_ifup(struct uip_driver_s *dev)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(skel->sk_txpoll, skeleton_WDDELAY, skel_polltimer, 1, (uint32_t)skel);
+
+ /* Enable the Ethernet interrupt */
+
+ skel->sk_bifup = true;
+ up_enable_irq(CONFIG_skeleton_IRQ);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: skel_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int skel_ifdown(struct uip_driver_s *dev)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+ up_disable_irq(CONFIG_skeleton_IRQ);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(skel->sk_txpoll);
+ wd_cancel(skel->sk_txtimeout);
+
+ /* Put the EMAC in its reset, non-operational state. This should be
+ * a known configuration that will guarantee the skel_ifup() always
+ * successfully brings the interface back up.
+ */
+
+ /* Mark the device "down" */
+
+ skel->sk_bifup = false;
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: skel_txavail
+ *
+ * Description:
+ * Driver callback invoked when new TX data is available. This is a
+ * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ * latency.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int skel_txavail(struct uip_driver_s *dev)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable interrupts because this function may be called from interrupt
+ * level processing.
+ */
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (skel->sk_bifup)
+ {
+ /* Check if there is room in the hardware to hold another outgoing packet. */
+
+ /* If so, then poll uIP for new XMIT data */
+
+ (void)uip_poll(&skel->sk_dev, skel_uiptxpoll);
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: skel_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int skel_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: skel_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int skel_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: skel_initialize
+ *
+ * Description:
+ * Initialize the Ethernet controller and driver
+ *
+ * Parameters:
+ * intf - In the case where there are multiple EMACs, this value
+ * identifies which EMAC is to be initialized.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int skel_initialize(int intf)
+{
+ struct skel_driver_s *priv;
+
+ /* Get the interface structure associated with this interface number. */
+
+ DEBUGASSERT(inf < CONFIG_skeleton_NINTERFACES);
+ priv = &g_skel[intf];
+
+ /* Check if a Ethernet chip is recognized at its I/O base */
+
+ /* Attach the IRQ to the driver */
+
+ if (irq_attach(CONFIG_skeleton_IRQ, skel_interrupt))
+ {
+ /* We could not attach the ISR to the interrupt */
+
+ return -EAGAIN;
+ }
+
+ /* Initialize the driver structure */
+
+ memset(priv, 0, sizeof(struct skel_driver_s));
+ priv->sk_dev.d_ifup = skel_ifup; /* I/F up (new IP address) callback */
+ priv->sk_dev.d_ifdown = skel_ifdown; /* I/F down callback */
+ priv->sk_dev.d_txavail = skel_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ priv->sk_dev.d_addmac = skel_addmac; /* Add multicast MAC address */
+ priv->sk_dev.d_rmmac = skel_rmmac; /* Remove multicast MAC address */
+#endif
+ priv->sk_dev.d_private = (void*)g_skel; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ priv->sk_txpoll = wd_create(); /* Create periodic poll timer */
+ priv->sk_txtimeout = wd_create(); /* Create TX timeout timer */
+
+ /* Put the interface in the down state. This usually amounts to resetting
+ * the device and/or calling skel_ifdown().
+ */
+
+ /* Read the MAC address from the hardware into priv->sk_dev.d_mac.ether_addr_octet */
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ (void)netdev_register(&priv->sk_dev);
+ return OK;
+}
+
+#endif /* CONFIG_NET && CONFIG_NET_skeleton */
diff --git a/nuttx/drivers/net/slip.c b/nuttx/drivers/net/slip.c
new file mode 100644
index 000000000..31f44cbb9
--- /dev/null
+++ b/nuttx/drivers/net/slip.c
@@ -0,0 +1,1017 @@
+/****************************************************************************
+ * drivers/net/slip.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Reference: RFC 1055
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/net/net.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#if defined(CONFIG_NET) && defined(CONFIG_NET_SLIP)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* NOTE: Slip requires UART hardware handshake. If hardware handshake is
+ * not available with your UART, then you might try the 'slattach' option
+ * -L which enable "3-wire operation." That allows operation without the
+ * hardware handshake (but with the possibility of data overrun).
+ */
+
+/* Configuration ************************************************************/
+
+#if UIP_LLH_LEN > 0
+# error "UIP_LLH_LEN must be set to zero"
+#endif
+
+#ifndef CONFIG_NET_NOINTS
+# warning "CONFIG_NET_NOINTS must be set"
+#endif
+
+#ifndef CONFIG_NET_MULTIBUFFER
+# warning "CONFIG_NET_MULTIBUFFER must be set"
+#endif
+
+#ifndef CONFIG_SLIP_STACKSIZE
+# define CONFIG_SLIP_STACKSIZE 2048
+#endif
+
+#ifndef CONFIG_SLIP_DEFPRIO
+# define CONFIG_SLIP_DEFPRIO 128
+#endif
+
+/* The Linux slip module hard-codes its MTU size to 296 (40 bytes for the
+ * IP+TPC headers plus 256 bytes of data). So you might as well set
+ * CONFIG_NET_BUFSIZE to 296 as well.
+ *
+ * There may be an issue with this setting, however. I see that Linux uses
+ * a MTU of 296 and window of 256, but actually only sends 168 bytes of data:
+ * 40 + 128. I believe that is to allow for the 2x worst cast packet
+ * expansion. Ideally we would like to advertise the 256 MSS, but restrict
+ * uIP to 128 bytes (possibly by modifying the uip_mss() macro).
+ */
+
+#if CONFIG_NET_BUFSIZE < 296
+# error "CONFIG_NET_BUFSIZE >= 296 is required"
+#elif CONFIG_NET_BUFSIZE > 296
+# warning "CONFIG_NET_BUFSIZE == 296 is optimal"
+#endif
+
+/* CONFIG_SLIP_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_SLIP_NINTERFACES
+# define CONFIG_SLIP_NINTERFACES 1
+#endif
+
+/* SLIP special character codes *******************************************/
+
+#define SLIP_END 0300 /* Indicates end of packet */
+#define SLIP_ESC 0333 /* Indicates byte stuffing */
+#define SLIP_ESC_END 0334 /* ESC ESC_END means SLIP_END data byte */
+#define SLIP_ESC_ESC 0335 /* ESC ESC_ESC means ESC data byte */
+
+/* General driver definitions **********************************************/
+
+/* TX poll delay = 1 second = 1000000 microseconds. */
+
+#define SLIP_WDDELAY (1*1000000)
+#define SLIP_POLLHSEC (1*2)
+
+/* Statistics helper */
+
+#ifdef CONFIG_NET_STATISTICS
+# define SLIP_STAT(p,f) (p->stats.f)++
+#else
+# define SLIP_STAT(p,f)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Driver statistics */
+
+#ifdef CONFIG_NET_STATISTICS
+struct slip_statistics_s
+{
+ uint32_t transmitted; /* Number of packets transmitted */
+ uint32_t received /* Number of packets received */
+};
+#endif
+
+/* The slip_driver_s encapsulates all state information for a single hardware
+ * interface
+ */
+
+struct slip_driver_s
+{
+ volatile bool bifup; /* true:ifup false:ifdown */
+ int fd; /* TTY file descriptor */
+ pid_t rxpid; /* Receiver thread ID */
+ pid_t txpid; /* Transmitter thread ID */
+ sem_t waitsem; /* Mutually exclusive access to uIP */
+ uint16_t rxlen; /* The number of bytes in rxbuf */
+
+ /* Driver statistics */
+
+#ifdef CONFIG_NET_STATISTICS
+ struct slip_statistics_s stats;
+#endif
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s dev; /* Interface understood by uIP */
+ uint8_t rxbuf[CONFIG_NET_BUFSIZE + 2];
+ uint8_t txbuf[CONFIG_NET_BUFSIZE + 2];
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+ /* We really should get rid of CONFIG_SLIP_NINTERFACES and, instead,
+ * kmalloc() new interface instances as needed.
+ */
+
+static struct slip_driver_s g_slip[CONFIG_SLIP_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void slip_semtake(FAR struct slip_driver_s *priv);
+
+/* Common TX logic */
+
+static void slip_write(FAR struct slip_driver_s *priv, const uint8_t *buffer, int len);
+static void slip_putc(FAR struct slip_driver_s *priv, int ch);
+static int slip_transmit(FAR struct slip_driver_s *priv);
+static int slip_uiptxpoll(struct uip_driver_s *dev);
+static void slip_txtask(int argc, char *argv[]);
+
+/* Packet receiver task */
+
+static int slip_getc(FAR struct slip_driver_s *priv);
+static inline void slip_receive(FAR struct slip_driver_s *priv);
+static int slip_rxtask(int argc, char *argv[]);
+
+/* NuttX callback functions */
+
+static int slip_ifup(struct uip_driver_s *dev);
+static int slip_ifdown(struct uip_driver_s *dev);
+static int slip_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int slip_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int slip_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: slip_semtake
+ ****************************************************************************/
+
+static void slip_semtake(FAR struct slip_driver_s *priv)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&priv->waitsem) != 0)
+ {
+ /* The only case that an error should occur here is if
+ * the wait was awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+#define slip_semgive(p) sem_post(&(p)->waitsem);
+
+/****************************************************************************
+ * Function: slip_write
+ *
+ * Description:
+ * Just an inline wrapper around fwrite with error checking.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * buffer - Buffer data to send
+ * len - Buffer length in bytes
+ *
+ ****************************************************************************/
+
+static inline void slip_write(FAR struct slip_driver_s *priv,
+ const uint8_t *buffer, int len)
+{
+ /* Handle the case where the write is awakened by a signal */
+
+ while (write(priv->fd, buffer, len) < 0)
+ {
+ DEBUGASSERT(errno == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Function: slip_putc
+ *
+ * Description:
+ * Just an inline wrapper around putc with error checking.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ch - The character to send
+ *
+ ****************************************************************************/
+
+static inline void slip_putc(FAR struct slip_driver_s *priv, int ch)
+{
+ uint8_t buffer = (uint8_t)ch;
+ slip_write(priv, &buffer, 1);
+}
+
+/****************************************************************************
+ * Function: slip_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int slip_transmit(FAR struct slip_driver_s *priv)
+{
+ uint8_t *src;
+ uint8_t *start;
+ uint8_t esc;
+ int remaining;
+ int len;
+
+ /* Increment statistics */
+
+ nvdbg("Sending packet size %d\n", priv->dev.d_len);
+ SLIP_STAT(priv, transmitted);
+
+ /* Send an initial END character to flush out any data that may have
+ * accumulated in the receiver due to line noise
+ */
+
+ slip_putc(priv, SLIP_END);
+
+ /* For each byte in the packet, send the appropriate character sequence */
+
+ src = priv->dev.d_buf;
+ remaining = priv->dev.d_len;
+ start = src;
+ len = 0;
+
+ while (remaining-- > 0)
+ {
+ switch (*src)
+ {
+ /* If it's the same code as an END character, we send a special two
+ * character code so as not to make the receiver think we sent an
+ * END
+ */
+
+ case SLIP_END:
+ esc = SLIP_ESC_END;
+ goto escape;
+
+ /* If it's the same code as an ESC character, we send a special two
+ * character code so as not to make the receiver think we sent an
+ * ESC
+ */
+
+ case SLIP_ESC:
+ esc = SLIP_ESC_ESC;
+
+ escape:
+ {
+ /* Flush any unsent data */
+
+ if (len > 0)
+ {
+ slip_write(priv, start, len);
+
+ /* Reset */
+
+ start = src + 1;
+ len = 0;
+ }
+
+ /* Then send the escape sequence */
+
+ slip_putc(priv, SLIP_ESC);
+ slip_putc(priv, esc);
+ }
+ break;
+
+ /* otherwise, just bump up the count */
+
+ default:
+ len++;
+ break;
+ }
+
+ /* Point to the next character in the packet */
+
+ src++;
+ }
+
+ /* We have looked at every character in the packet. Now flush any unsent
+ * data
+ */
+
+ if (len > 0)
+ {
+ slip_write(priv, start, len);
+ }
+
+ /* And send the END token */
+
+ slip_putc(priv, SLIP_END);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: slip_uiptxpoll
+ *
+ * Description:
+ * Check if uIP has any outgoing packets ready to send. This is a
+ * callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete, or
+ * 2. When the preceding TX packet send times o ]ut and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * The initiator of the poll holds the priv->waitsem;
+ *
+ ****************************************************************************/
+
+static int slip_uiptxpoll(struct uip_driver_s *dev)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ /* If the polling resulted in data that should be sent out on the network,
+ * the field d_len is set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ slip_transmit(priv);
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: slip_txtask
+ *
+ * Description:
+ * Polling and transmission is performed on tx thread.
+ *
+ * Parameters:
+ * arg - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void slip_txtask(int argc, char *argv[])
+{
+ FAR struct slip_driver_s *priv;
+ unsigned int index = *(argv[1]) - '0';
+ uip_lock_t flags;
+
+ ndbg("index: %d\n", index);
+ DEBUGASSERT(index < CONFIG_SLIP_NINTERFACES);
+
+ /* Get our private data structure instance and wake up the waiting
+ * initialization logic.
+ */
+
+ priv = &g_slip[index];
+ slip_semgive(priv);
+
+ /* Loop forever */
+
+ for (;;)
+ {
+ /* Wait for the timeout to expire (or until we are signaled by by */
+
+ usleep(SLIP_WDDELAY);
+
+ /* Is the interface up? */
+
+ if (priv->bifup)
+ {
+ /* Get exclusive access to uIP (if it it is already being used
+ * slip_rxtask, then we have to wait).
+ */
+
+ slip_semtake(priv);
+
+ /* Poll uIP for new XMIT data. BUG: We really need to calculate
+ * the number of hsecs! When we are awakened by slip_txavail, the
+ * number will be smaller; when we have to wait for the semaphore
+ * (above), it may be larger.
+ */
+
+ flags = uip_lock();
+ priv->dev.d_buf = priv->txbuf;
+ (void)uip_timer(&priv->dev, slip_uiptxpoll, SLIP_POLLHSEC);
+ uip_unlock(flags);
+ slip_semgive(priv);
+ }
+ }
+}
+
+/****************************************************************************
+ * Function: slip_getc
+ *
+ * Description:
+ * Get one byte from the serial input.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * The returned byte
+ *
+ ****************************************************************************/
+
+static inline int slip_getc(FAR struct slip_driver_s *priv)
+{
+ uint8_t ch;
+
+ while (read(priv->fd, &ch, 1) < 0)
+ {
+ DEBUGASSERT(errno == EINTR);
+ }
+
+ return (int)ch;
+}
+
+/****************************************************************************
+ * Function: slip_receive
+ *
+ * Description:
+ * Read a packet from the serial input
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static inline void slip_receive(FAR struct slip_driver_s *priv)
+{
+ uint8_t ch;
+
+ /* Copy the data data from the hardware to to the RX buffer until we
+ * put together a whole packet. Make sure not to copy them into the
+ * packet if we run out of room.
+ */
+
+ nvdbg("Receiving packet\n");
+ for (;;)
+ {
+ /* Get the next character in the stream. */
+
+ ch = slip_getc(priv);
+
+ /* Handle bytestuffing if necessary */
+
+ switch (ch)
+ {
+ /* If it's an END character then we're done with the packet.
+ * (OR we are just starting a packet)
+ */
+
+ case SLIP_END:
+ nvdbg("END\n");
+
+ /* A minor optimization: if there is no data in the packet, ignore
+ * it. This is meant to avoid bothering IP with all the empty
+ * packets generated by the duplicate END characters which are in
+ * turn sent to try to detect line noise.
+ */
+
+ if (priv->rxlen > 0)
+ {
+ nvdbg("Received packet size %d\n", priv->rxlen);
+ return;
+ }
+ break;
+
+ /* if it's the same code as an ESC character, wait and get another
+ * character and then figure out what to store in the packet based
+ * on that.
+ */
+
+ case SLIP_ESC:
+ nvdbg("ESC\n");
+ ch = slip_getc(priv);
+
+ /* if "ch" is not one of these two, then we have a protocol
+ * violation. The best bet seems to be to leave the byte alone
+ * and just stuff it into the packet
+ */
+
+ switch (ch)
+ {
+ case SLIP_ESC_END:
+ nvdbg("ESC-END\n");
+ ch = SLIP_END;
+ break;
+ case SLIP_ESC_ESC:
+ nvdbg("ESC-ESC\n");
+ ch = SLIP_ESC;
+ break;
+ default:
+ ndbg("ERROR: Protocol violation: %02x\n", ch);
+ break;
+ }
+
+ /* Here we fall into the default handler and let it store the
+ * character for us
+ */
+
+ default:
+ if (priv->rxlen < CONFIG_NET_BUFSIZE+2)
+ {
+ priv->rxbuf[priv->rxlen++] = ch;
+ }
+ break;
+ }
+ }
+}
+
+/****************************************************************************
+ * Function: slip_rxtask
+ *
+ * Description:
+ * Wait for incoming data.
+ *
+ * Parameters:
+ * argc
+ * argv
+ *
+ * Returned Value:
+ * (Does not return)
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int slip_rxtask(int argc, char *argv[])
+{
+ FAR struct slip_driver_s *priv;
+ unsigned int index = *(argv[1]) - '0';
+ uip_lock_t flags;
+ int ch;
+
+ ndbg("index: %d\n", index);
+ DEBUGASSERT(index < CONFIG_SLIP_NINTERFACES);
+
+ /* Get our private data structure instance and wake up the waiting
+ * initialization logic.
+ */
+
+ priv = &g_slip[index];
+ slip_semgive(priv);
+
+ /* Loop forever */
+
+ for (;;)
+ {
+ /* Wait for the next character to be available on the input stream. */
+
+ nvdbg("Waiting...\n");
+ ch = slip_getc(priv);
+
+ /* Ignore any input that we receive before the interface is up. */
+
+ if (!priv->bifup)
+ {
+ continue;
+ }
+
+ /* We have something...
+ *
+ * END characters may appear at packet boundaries BEFORE as well as
+ * after the beginning of the packet. This is normal and expected.
+ */
+
+ if (ch == SLIP_END)
+ {
+ priv->rxlen = 0;
+ }
+
+ /* Otherwise, we are in danger of being out-of-sync. Apparently the
+ * leading END character is optional. Let's try to continue.
+ */
+
+ else
+ {
+ priv->rxbuf[0] = (uint8_t)ch;
+ priv->rxlen = 1;
+ }
+
+ /* Copy the data data from the hardware to priv->rxbuf until we put
+ * together a whole packet.
+ */
+
+ slip_receive(priv);
+ SLIP_STAT(priv, received);
+
+ /* All packets are assumed to be IP packets (we don't have a choice..
+ * there is no Ethernet header containing the EtherType). So pass the
+ * received packet on for IP processing -- but only if it is big
+ * enough to hold an IP header.
+ */
+
+ if (priv->rxlen >= UIP_IPH_LEN)
+ {
+ /* Handle the IP input. Get exclusive access to uIP. */
+
+ slip_semtake(priv);
+ priv->dev.d_buf = priv->rxbuf;
+ priv->dev.d_len = priv->rxlen;
+
+ flags = uip_lock();
+ uip_input(&priv->dev);
+
+ /* If the above function invocation resulted in data that should
+ * be sent out on the network, the field d_len will set to a
+ * value > 0. NOTE that we are transmitting using the RX buffer!
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ slip_transmit(priv);
+ }
+ uip_unlock(flags);
+ slip_semgive(priv);
+ }
+ else
+ {
+ SLIP_STAT(priv, rxsmallpacket);
+ }
+ }
+
+ /* We won't get here */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: slip_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int slip_ifup(struct uip_driver_s *dev)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Mark the interface up */
+
+ priv->bifup = true;
+ return OK;
+}
+
+/****************************************************************************
+ * Function: slip_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int slip_ifdown(struct uip_driver_s *dev)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ /* Mark the device "down" */
+
+ priv->bifup = false;
+ return OK;
+}
+
+/****************************************************************************
+ * Function: slip_txavail
+ *
+ * Description:
+ * Driver callback invoked when new TX data is available. This is a
+ * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ * latency.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static int slip_txavail(struct uip_driver_s *dev)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (priv->bifup)
+ {
+ /* Wake up the TX polling thread */
+
+ kill(priv->txpid, SIGALRM);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: slip_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int slip_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: slip_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int slip_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: slip_initialize
+ *
+ * Description:
+ * Instantiate a SLIP network interface.
+ *
+ * Parameters:
+ * intf - In the case where there are multiple SLIP interfaces, this value
+ * identifies which is to be initialized. The network name will be,
+ * for example, "/dev/slip5" for intf == 5
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int slip_initialize(int intf, const char *devname)
+{
+ struct slip_driver_s *priv;
+ char buffer[8];
+ const char *argv[2];
+
+ /* Get the interface structure associated with this interface number. */
+
+ DEBUGASSERT(intf < CONFIG_SLIP_NINTERFACES);
+ priv = &g_slip[intf];
+
+ /* Initialize the driver structure */
+
+ memset(priv, 0, sizeof(struct slip_driver_s));
+ priv->dev.d_ifup = slip_ifup; /* I/F up (new IP address) callback */
+ priv->dev.d_ifdown = slip_ifdown; /* I/F down callback */
+ priv->dev.d_txavail = slip_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ priv->dev.d_addmac = slip_addmac; /* Add multicast MAC address */
+ priv->dev.d_rmmac = slip_rmmac; /* Remove multicast MAC address */
+#endif
+ priv->dev.d_private = priv; /* Used to recover private state from dev */
+
+ /* Open the device */
+
+ priv->fd = open(devname, O_RDWR, 0666);
+ if (priv->fd < 0)
+ {
+ ndbg("ERROR: Failed to open %s: %d\n", devname, errno);
+ return -errno;
+ }
+
+ /* Initialize the wait semaphore */
+
+ sem_init(&priv->waitsem, 0, 0);
+
+ /* Put the interface in the down state. This usually amounts to resetting
+ * the device and/or calling slip_ifdown().
+ */
+
+ slip_ifdown(&priv->dev);
+
+ /* Start the SLIP receiver task */
+
+ snprintf(buffer, 8, "%d", intf);
+ argv[0] = buffer;
+ argv[1] = NULL;
+
+#ifndef CONFIG_CUSTOM_STACK
+ priv->rxpid = task_create("rxslip", CONFIG_SLIP_DEFPRIO,
+ CONFIG_SLIP_STACKSIZE, (main_t)slip_rxtask, argv);
+#else
+ priv->rxpid = task_create("rxslip", CONFIG_SLIP_DEFPRIO,
+ (main_t)slip_rxtask, argv);
+#endif
+ if (priv->rxpid < 0)
+ {
+ ndbg("ERROR: Failed to start receiver task\n");
+ return -errno;
+ }
+
+ /* Wait and make sure that the receive task is started. */
+
+ slip_semtake(priv);
+
+ /* Start the SLIP transmitter task */
+
+#ifndef CONFIG_CUSTOM_STACK
+ priv->txpid = task_create("txslip", CONFIG_SLIP_DEFPRIO,
+ CONFIG_SLIP_STACKSIZE, (main_t)slip_txtask, argv);
+#else
+ priv->txpid = task_create("txslip", CONFIG_SLIP_DEFPRIO,
+ (main_t)slip_txtask, argv);
+#endif
+ if (priv->txpid < 0)
+ {
+ ndbg("ERROR: Failed to start receiver task\n");
+ return -errno;
+ }
+
+ /* Wait and make sure that the transmit task is started. */
+
+ slip_semtake(priv);
+
+ /* Bump the semaphore count so that it can now be used as a mutex */
+
+ slip_semgive(priv);
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ (void)netdev_register(&priv->dev);
+
+ /* When the RX and TX tasks were created, the TTY file descriptor was
+ * dup'ed for each task. This task no longer needs the file descriptor
+ * and we can safely close it.
+ */
+
+ close(priv->fd);
+ return OK;
+}
+
+#endif /* CONFIG_NET && CONFIG_NET_SLIP */
+
diff --git a/nuttx/drivers/net/vnet.c b/nuttx/drivers/net/vnet.c
new file mode 100644
index 000000000..f1e2465b9
--- /dev/null
+++ b/nuttx/drivers/net/vnet.c
@@ -0,0 +1,673 @@
+/****************************************************************************
+ * drivers/net/vnet.c
+ *
+ * Copyright (C) 2011 Yu Qiang. All rights reserved.
+ * Author: Yu Qiang <yuq825@gmail.com>
+ *
+ * This file is a part of NuttX:
+ *
+ * Copyright (C) 2011 Gregory Nutt. 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#if defined(CONFIG_NET) && defined(CONFIG_NET_VNET)
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#include <rgmp/vnet.h>
+#include <rgmp/stdio.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* CONFIG_VNET_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_VNET_NINTERFACES
+# define CONFIG_VNET_NINTERFACES 1
+#endif
+
+/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define VNET_WDDELAY (1*CLK_TCK)
+#define VNET_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define VNET_TXTIMEOUT (60*CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)vnet->sk_dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The vnet_driver_s encapsulates all state information for a single hardware
+ * interface
+ */
+
+struct vnet_driver_s
+{
+ bool sk_bifup; /* true:ifup false:ifdown */
+ WDOG_ID sk_txpoll; /* TX poll timer */
+ //WDOG_ID sk_txtimeout; /* TX timeout timer */
+
+ /* This holds the information visible to uIP/NuttX */
+ struct rgmp_vnet *vnet;
+ struct uip_driver_s sk_dev; /* Interface understood by uIP */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct vnet_driver_s g_vnet[CONFIG_VNET_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static int vnet_transmit(FAR struct vnet_driver_s *vnet);
+static int vnet_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void vnet_txdone(FAR struct vnet_driver_s *vnet);
+
+/* Watchdog timer expirations */
+
+static void vnet_polltimer(int argc, uint32_t arg, ...);
+static void vnet_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int vnet_ifup(struct uip_driver_s *dev);
+static int vnet_ifdown(struct uip_driver_s *dev);
+static int vnet_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int vnet_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int vnet_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: vnet_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * vnet - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int vnet_transmit(FAR struct vnet_driver_s *vnet)
+{
+ int err;
+
+ /* Verify that the hardware is ready to send another packet. If we get
+ * here, then we are committed to sending a packet; Higher level logic
+ * must have assured that there is not transmission in progress.
+ */
+
+ /* Increment statistics */
+
+ /* Send the packet: address=vnet->sk_dev.d_buf, length=vnet->sk_dev.d_len */
+ err = vnet_xmit(vnet->vnet, (char *)vnet->sk_dev.d_buf, vnet->sk_dev.d_len);
+ if (err) {
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+ //(void)wd_start(vnet->sk_txtimeout, VNET_TXTIMEOUT, vnet_txtimeout, 1, (uint32_t)vnet);
+
+ // When vnet_xmit fail, it means TX buffer is full. Watchdog
+ // is of no use here because no TX done INT will happen. So
+ // we reset the TX buffer directly.
+#ifdef CONFIG_DEBUG
+ cprintf("VNET: TX buffer is full\n");
+#endif
+ return ERROR;
+ }
+ else {
+ // this step may be unnecessary here
+ vnet_txdone(vnet);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: vnet_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timesout and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int vnet_uiptxpoll(struct uip_driver_s *dev)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+
+ /* If the polling resulted in data that should be sent out on the network,
+ * the field d_len is set to a value > 0.
+ */
+
+ if (vnet->sk_dev.d_len > 0)
+ {
+ uip_arp_out(&vnet->sk_dev);
+ vnet_transmit(vnet);
+
+ /* Check if there is room in the device to hold another packet. If not,
+ * return a non-zero value to terminate the poll.
+ */
+ if (vnet_is_txbuff_full(vnet->vnet))
+ return 1;
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: rtos_vnet_recv
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * vnet - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+void rtos_vnet_recv(struct rgmp_vnet *vnet_dummy, char *data, int len)
+{
+ // now only support 1 vnet
+ struct vnet_driver_s *vnet = &g_vnet[0];
+
+ do {
+ /* Check for errors and update statistics */
+
+ /* Check if the packet is a valid size for the uIP buffer configuration */
+ if (len > CONFIG_NET_BUFSIZE || len < 14) {
+#ifdef CONFIG_DEBUG
+ cprintf("VNET: receive invalid packet of size %d\n", len);
+#endif
+ return;
+ }
+
+ // Copy the data data from the hardware to vnet->sk_dev.d_buf. Set
+ // amount of data in vnet->sk_dev.d_len
+ memcpy(vnet->sk_dev.d_buf, data, len);
+ vnet->sk_dev.d_len = len;
+
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ uip_arp_ipin(&vnet->sk_dev);
+ uip_input(&vnet->sk_dev);
+
+ // If the above function invocation resulted in data that should be
+ // sent out on the network, the field d_len will set to a value > 0.
+ if (vnet->sk_dev.d_len > 0) {
+ uip_arp_out(&vnet->sk_dev);
+ vnet_transmit(vnet);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP)) {
+ uip_arp_arpin(&vnet->sk_dev);
+
+ // If the above function invocation resulted in data that should be
+ // sent out on the network, the field d_len will set to a value > 0.
+ if (vnet->sk_dev.d_len > 0) {
+ vnet_transmit(vnet);
+ }
+ }
+ }
+ while (0); /* While there are more packets to be processed */
+}
+
+/****************************************************************************
+ * Function: vnet_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Parameters:
+ * vnet - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void vnet_txdone(FAR struct vnet_driver_s *vnet)
+{
+ /* Check for errors and update statistics */
+
+ /* If no further xmits are pending, then cancel the TX timeout and
+ * disable further Tx interrupts.
+ */
+
+ //wd_cancel(vnet->sk_txtimeout);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&vnet->sk_dev, vnet_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: vnet_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void vnet_txtimeout(int argc, uint32_t arg, ...)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)arg;
+
+ /* Increment statistics and dump debug info */
+
+ /* Then reset the hardware */
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&vnet->sk_dev, vnet_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: vnet_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void vnet_polltimer(int argc, uint32_t arg, ...)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)arg;
+
+ /* Check if there is room in the send another TX packet. We cannot perform
+ * the TX poll if he are unable to accept another packet for transmission.
+ */
+ if (vnet_is_txbuff_full(vnet->vnet)) {
+#ifdef CONFIG_DEBUG
+ cprintf("VNET: TX buffer is full\n");
+#endif
+ return;
+ }
+
+ /* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm..
+ * might be bug here. Does this mean if there is a transmit in progress,
+ * we will missing TCP time state updates?
+ */
+
+ (void)uip_timer(&vnet->sk_dev, vnet_uiptxpoll, VNET_POLLHSEC);
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(vnet->sk_txpoll, VNET_WDDELAY, vnet_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: vnet_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int vnet_ifup(struct uip_driver_s *dev)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(vnet->sk_txpoll, VNET_WDDELAY, vnet_polltimer, 1, (uint32_t)vnet);
+
+ vnet->sk_bifup = true;
+ return OK;
+}
+
+/****************************************************************************
+ * Function: vnet_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int vnet_ifdown(struct uip_driver_s *dev)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(vnet->sk_txpoll);
+ //wd_cancel(vnet->sk_txtimeout);
+
+ /* Put the the EMAC is its reset, non-operational state. This should be
+ * a known configuration that will guarantee the vnet_ifup() always
+ * successfully brings the interface back up.
+ */
+
+ /* Mark the device "down" */
+
+ vnet->sk_bifup = false;
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: vnet_txavail
+ *
+ * Description:
+ * Driver callback invoked when new TX data is available. This is a
+ * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ * latency.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int vnet_txavail(struct uip_driver_s *dev)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable interrupts because this function may be called from interrupt
+ * level processing.
+ */
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (vnet->sk_bifup)
+ {
+ /* Check if there is room in the hardware to hold another outgoing packet. */
+ if (vnet_is_txbuff_full(vnet->vnet)) {
+#ifdef CONFIG_DEBUG
+ cprintf("VNET: TX buffer is full\n");
+#endif
+ goto out;
+ }
+
+ /* If so, then poll uIP for new XMIT data */
+
+ (void)uip_poll(&vnet->sk_dev, vnet_uiptxpoll);
+ }
+
+ out:
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: vnet_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int vnet_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: vnet_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int vnet_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: vnet_initialize
+ *
+ * Description:
+ * Initialize the Ethernet controller and driver
+ *
+ * Parameters:
+ * intf - In the case where there are multiple EMACs, this value
+ * identifies which EMAC is to be initialized.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+void vnet_initialize(void)
+{
+ struct vnet_driver_s *priv;
+ struct rgmp_vnet *vnet = vnet_list.next;
+ int i;
+
+ for (i=0; i<CONFIG_VNET_NINTERFACES; i++) {
+ if (vnet == NULL)
+ break;
+ priv = &g_vnet[i];
+
+ /* Initialize the driver structure */
+
+ memset(priv, 0, sizeof(struct vnet_driver_s));
+ priv->sk_dev.d_ifup = vnet_ifup; /* I/F down callback */
+ priv->sk_dev.d_ifdown = vnet_ifdown; /* I/F up (new IP address) callback */
+ priv->sk_dev.d_txavail = vnet_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ priv->sk_dev.d_addmac = vnet_addmac; /* Add multicast MAC address */
+ priv->sk_dev.d_rmmac = vnet_rmmac; /* Remove multicast MAC address */
+#endif
+ priv->sk_dev.d_private = (void*)g_vnet; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ priv->sk_txpoll = wd_create(); /* Create periodic poll timer */
+ //priv->sk_txtimeout = wd_create(); /* Create TX timeout timer */
+
+ priv->vnet = vnet;
+
+ /* Register the device with the OS */
+
+ (void)netdev_register(&priv->sk_dev);
+ vnet = vnet->next;
+ }
+}
+
+#endif /* CONFIG_NET && CONFIG_NET_VNET */
diff --git a/nuttx/drivers/pipes/Kconfig b/nuttx/drivers/pipes/Kconfig
new file mode 100644
index 000000000..ae2bf3130
--- /dev/null
+++ b/nuttx/drivers/pipes/Kconfig
@@ -0,0 +1,4 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
diff --git a/nuttx/drivers/pipes/Make.defs b/nuttx/drivers/pipes/Make.defs
new file mode 100644
index 000000000..836505481
--- /dev/null
+++ b/nuttx/drivers/pipes/Make.defs
@@ -0,0 +1,46 @@
+############################################################################
+# drivers/pipes/Make.defs
+#
+# Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+ifneq ($(CONFIG_NFILE_DESCRIPTORS),0)
+
+# Include pipe driver
+
+CSRCS += pipe.c fifo.c pipe_common.c
+
+# Include pipe build support
+
+DEPPATH += --dep-path pipes
+VPATH += :pipes
+endif
diff --git a/nuttx/drivers/pipes/fifo.c b/nuttx/drivers/pipes/fifo.c
new file mode 100644
index 000000000..03aafd0f0
--- /dev/null
+++ b/nuttx/drivers/pipes/fifo.c
@@ -0,0 +1,139 @@
+/****************************************************************************
+ * drivers/pipes/fifo.c
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <nuttx/fs/fs.h>
+#include <errno.h>
+
+#include "pipe_common.h"
+
+#if CONFIG_DEV_PIPE_SIZE > 0
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations fifo_fops =
+{
+ pipecommon_open, /* open */
+ pipecommon_close, /* close */
+ pipecommon_read, /* read */
+ pipecommon_write, /* write */
+ 0, /* seek */
+ 0 /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , pipecommon_poll /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mkfifo
+ *
+ * Description:
+ * mkfifo() makes a FIFO device driver file with name 'pathname.' Unlike
+ * Linux, a NuttX FIFO is not a special file type but simply a device driver
+ * instance. 'mode' specifies the FIFO's permissions.
+ *
+ * Once the FIFO has been created by mkfifo(), any thread can open it for
+ * reading or writing, in the same way as an ordinary file. However, it must
+ * have been opened from both reading and writing before input or output
+ * can be performed. This FIFO implementation will block all attempts to
+ * open a FIFO read-only until at least one thread has opened the FIFO for
+ * writing.
+ *
+ * If all threads that write to the FIFO have closed, subsequent calls to
+ * read() on the FIFO will return 0 (end-of-file).
+ *
+ * Inputs:
+ * pathname - The full path to the FIFO instance to attach to or to create
+ * (if not already created).
+ * mode - Ignored for now
+ *
+ * Return:
+ * 0 is returned on success; otherwise, -1 is returned with errno set
+ * appropriately.
+ *
+ ****************************************************************************/
+
+int mkfifo(FAR const char *pathname, mode_t mode)
+{
+ struct pipe_dev_s *dev;
+ int ret;
+
+ /* Allocate and initialize a new device structure instance */
+
+ dev = pipecommon_allocdev();
+ if (!dev)
+ {
+ return -ENOMEM;
+ }
+
+ ret = register_driver(pathname, &fifo_fops, mode, (void*)dev);
+ if (ret != 0)
+ {
+ pipecommon_freedev(dev);
+ }
+
+ return ret;
+}
+
+#endif /* CONFIG_DEV_PIPE_SIZE > 0 */
diff --git a/nuttx/drivers/pipes/pipe.c b/nuttx/drivers/pipes/pipe.c
new file mode 100644
index 000000000..20c160475
--- /dev/null
+++ b/nuttx/drivers/pipes/pipe.c
@@ -0,0 +1,286 @@
+/****************************************************************************
+ * drivers/pipes/pipe.c
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Compilation Switches
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <nuttx/fs/fs.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "pipe_common.h"
+
+#if CONFIG_DEV_PIPE_SIZE > 0
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+#define MAX_PIPES 32
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int pipe_close(FAR struct file *filep);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations pipe_fops =
+{
+ pipecommon_open, /* open */
+ pipe_close, /* close */
+ pipecommon_read, /* read */
+ pipecommon_write, /* write */
+ 0, /* seek */
+ 0 /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , pipecommon_poll /* poll */
+#endif
+};
+
+static sem_t g_pipesem = { 1 };
+static uint32_t g_pipeset = 0;
+static uint32_t g_pipecreated = 0;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pipe_allocate
+ ****************************************************************************/
+
+static inline int pipe_allocate(void)
+{
+ int pipeno;
+ int ret = -ENFILE;
+
+ for (pipeno = 0; pipeno < MAX_PIPES; pipeno++)
+ {
+ if ((g_pipeset & (1 << pipeno)) == 0)
+ {
+ g_pipeset |= (1 << pipeno);
+ ret = pipeno;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: pipe_free
+ ****************************************************************************/
+
+static inline void pipe_free(int pipeno)
+{
+ int ret = sem_wait(&g_pipesem);
+ if (ret == 0)
+ {
+ g_pipeset &= ~(1 << pipeno);
+ (void)sem_post(&g_pipesem);
+ }
+}
+
+/****************************************************************************
+ * Name: pipe_close
+ ****************************************************************************/
+
+static int pipe_close(FAR struct file *filep)
+{
+ struct inode *inode = filep->f_inode;
+ struct pipe_dev_s *dev = inode->i_private;
+ int ret;
+
+ /* Some sanity checking */
+#if CONFIG_DEBUG
+ if (!dev)
+ {
+ return -EBADF;
+ }
+#endif
+
+ /* Perform common close operations */
+
+ ret = pipecommon_close(filep);
+ if (ret == 0 && dev->d_refs == 0)
+ {
+ /* Release the pipe when there are no further open references to it. */
+
+ pipe_free(dev->d_pipeno);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pipe
+ *
+ * Description:
+ * pipe() creates a pair of file descriptors, pointing to a pipe inode, and
+ * places them in the array pointed to by 'filedes'. filedes[0] is for reading,
+ * filedes[1] is for writing.
+ *
+ * Inputs:
+ * filedes[2] - The user provided array in which to catch the pipe file
+ * descriptors
+ *
+ * Return:
+ * 0 is returned on success; otherwise, -1 is returned with errno set
+ * appropriately.
+ *
+ ****************************************************************************/
+
+int pipe(int filedes[2])
+{
+ struct pipe_dev_s *dev = NULL;
+ char devname[16];
+ int pipeno;
+ int err;
+ int ret;
+
+ /* Get exclusive access to the pipe allocation data */
+
+ ret = sem_wait(&g_pipesem);
+ if (ret < 0)
+ {
+ /* sem_wait() will have already set errno */
+
+ return ERROR;
+ }
+
+ /* Allocate a minor number for the pipe device */
+
+ pipeno = pipe_allocate();
+ if (pipeno < 0)
+ {
+ (void)sem_post(&g_pipesem);
+ err = -pipeno;
+ goto errout;
+ }
+
+ /* Create a pathname to the pipe device */
+
+ sprintf(devname, "/dev/pipe%d", pipeno);
+
+ /* Check if the pipe device has already been created */
+
+ if ((g_pipecreated & (1 << pipeno)) == 0)
+ {
+ /* No.. Allocate and initialize a new device structure instance */
+
+ dev = pipecommon_allocdev();
+ if (!dev)
+ {
+ (void)sem_post(&g_pipesem);
+ err = ENOMEM;
+ goto errout_with_pipe;
+ }
+
+ dev->d_pipeno = pipeno;
+
+ /* Register the pipe device */
+
+ ret = register_driver(devname, &pipe_fops, 0666, (void*)dev);
+ if (ret != 0)
+ {
+ (void)sem_post(&g_pipesem);
+ err = -ret;
+ goto errout_with_dev;
+ }
+
+ /* Remember that we created this device */
+
+ g_pipecreated |= (1 << pipeno);
+ }
+
+ (void)sem_post(&g_pipesem);
+
+ /* Get a write file descriptor */
+
+ filedes[1] = open(devname, O_WRONLY);
+ if (filedes[1] < 0)
+ {
+ err = -filedes[1];
+ goto errout_with_driver;
+ }
+
+ /* Get a read file descriptor */
+
+ filedes[0] = open(devname, O_RDONLY);
+ if (filedes[0] < 0)
+ {
+ err = -filedes[0];
+ goto errout_with_wrfd;
+ }
+
+ return OK;
+
+errout_with_wrfd:
+ close(filedes[1]);
+errout_with_driver:
+ unregister_driver(devname);
+errout_with_dev:
+ pipecommon_freedev(dev);
+errout_with_pipe:
+ pipe_free(pipeno);
+errout:
+ errno = err;
+ return ERROR;
+}
+
+#endif /* CONFIG_DEV_PIPE_SIZE > 0 */
diff --git a/nuttx/drivers/pipes/pipe_common.c b/nuttx/drivers/pipes/pipe_common.c
new file mode 100644
index 000000000..5f61fdd8e
--- /dev/null
+++ b/nuttx/drivers/pipes/pipe_common.c
@@ -0,0 +1,682 @@
+/****************************************************************************
+ * drivers/pipes/pipe_common.c
+ *
+ * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#if CONFIG_DEBUG
+# include <nuttx/arch.h>
+#endif
+
+#include "pipe_common.h"
+
+#if CONFIG_DEV_PIPE_SIZE > 0
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* CONFIG_DEV_PIPEDUMP will dump the contents of each transfer into and out
+ * of the pipe.
+ */
+
+#ifdef CONFIG_DEV_PIPEDUMP
+# define pipe_dumpbuffer(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+# define pipe_dumpbuffer(m,a,n)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void pipecommon_semtake(sem_t *sem);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pipecommon_semtake
+ ****************************************************************************/
+
+static void pipecommon_semtake(sem_t *sem)
+{
+ while (sem_wait(sem) != 0)
+ {
+ /* The only case that an error should occur here is if the wait was
+ * awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: pipecommon_pollnotify
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static void pipecommon_pollnotify(FAR struct pipe_dev_s *dev, pollevent_t eventset)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_DEV_PIPE_NPOLLWAITERS; i++)
+ {
+ struct pollfd *fds = dev->d_fds[i];
+ if (fds)
+ {
+ fds->revents |= (fds->events & eventset);
+ if (fds->revents != 0)
+ {
+ fvdbg("Report events: %02x\n", fds->revents);
+ sem_post(fds->sem);
+ }
+ }
+ }
+}
+#else
+# define pipecommon_pollnotify(dev,event)
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pipecommon_allocdev
+ ****************************************************************************/
+
+FAR struct pipe_dev_s *pipecommon_allocdev(void)
+{
+ struct pipe_dev_s *dev;
+
+ /* Allocate a private structure to manage the pipe */
+
+ dev = (struct pipe_dev_s *)kmalloc(sizeof(struct pipe_dev_s));
+ if (dev)
+ {
+ /* Initialize the private structure */
+
+ memset(dev, 0, sizeof(struct pipe_dev_s));
+ sem_init(&dev->d_bfsem, 0, 1);
+ sem_init(&dev->d_rdsem, 0, 0);
+ sem_init(&dev->d_wrsem, 0, 0);
+ }
+
+ return dev;
+}
+
+/****************************************************************************
+ * Name: pipecommon_freedev
+ ****************************************************************************/
+
+void pipecommon_freedev(FAR struct pipe_dev_s *dev)
+{
+ sem_destroy(&dev->d_bfsem);
+ sem_destroy(&dev->d_rdsem);
+ sem_destroy(&dev->d_wrsem);
+ kfree(dev);
+}
+
+/****************************************************************************
+ * Name: pipecommon_open
+ ****************************************************************************/
+
+int pipecommon_open(FAR struct file *filep)
+{
+ struct inode *inode = filep->f_inode;
+ struct pipe_dev_s *dev = inode->i_private;
+ int sval;
+ int ret;
+
+ /* Some sanity checking */
+#if CONFIG_DEBUG
+ if (!dev)
+ {
+ return -EBADF;
+ }
+#endif
+
+ /* Make sure that we have exclusive access to the device structure. The
+ * sem_wait() call should fail only if we are awakened by a signal.
+ */
+
+ ret = sem_wait(&dev->d_bfsem);
+ if (ret != OK)
+ {
+ fdbg("sem_wait failed: %d\n", errno);
+ DEBUGASSERT(errno > 0);
+ return -errno;
+ }
+
+ /* If this the first reference on the device, then allocate the buffer */
+
+ if (dev->d_refs == 0)
+ {
+ dev->d_buffer = (uint8_t*)kmalloc(CONFIG_DEV_PIPE_SIZE);
+ if (!dev->d_buffer)
+ {
+ (void)sem_post(&dev->d_bfsem);
+ return -ENOMEM;
+ }
+ }
+
+ /* Increment the reference count on the pipe instance */
+
+ dev->d_refs++;
+
+ /* If opened for writing, increment the count of writers on on the pipe instance */
+
+ if ((filep->f_oflags & O_WROK) != 0)
+ {
+ dev->d_nwriters++;
+
+ /* If this this is the first writer, then the read semaphore indicates the
+ * number of readers waiting for the first writer. Wake them all up.
+ */
+
+ if (dev->d_nwriters == 1)
+ {
+ while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0)
+ {
+ sem_post(&dev->d_rdsem);
+ }
+ }
+ }
+
+ /* If opened for read-only, then wait for at least one writer on the pipe */
+
+ sched_lock();
+ (void)sem_post(&dev->d_bfsem);
+ if ((filep->f_oflags & O_RDWR) == O_RDONLY && dev->d_nwriters < 1)
+ {
+ /* NOTE: d_rdsem is normally used when the read logic waits for more
+ * data to be written. But until the first writer has opened the
+ * pipe, the meaning is different: it is used prevent O_RDONLY open
+ * calls from returning until there is at least one writer on the pipe.
+ * This is required both by spec and also because it prevents
+ * subsequent read() calls from returning end-of-file because there is
+ * no writer on the pipe.
+ */
+
+ ret = sem_wait(&dev->d_rdsem);
+ if (ret != OK)
+ {
+ /* The sem_wait() call should fail only if we are awakened by
+ * a signal.
+ */
+
+ fdbg("sem_wait failed: %d\n", errno);
+ DEBUGASSERT(errno > 0);
+ ret = -errno;
+
+ /* Immediately close the pipe that we just opened */
+
+ (void)pipecommon_close(filep);
+ }
+ }
+
+ sched_unlock();
+ return ret;
+}
+
+/****************************************************************************
+ * Name: pipecommon_close
+ ****************************************************************************/
+
+int pipecommon_close(FAR struct file *filep)
+{
+ struct inode *inode = filep->f_inode;
+ struct pipe_dev_s *dev = inode->i_private;
+ int sval;
+
+ /* Some sanity checking */
+#if CONFIG_DEBUG
+ if (!dev)
+ {
+ return -EBADF;
+ }
+#endif
+
+ /* Make sure that we have exclusive access to the device structure.
+ * NOTE: close() is supposed to return EINTR if interrupted, however
+ * I've never seen anyone check that.
+ */
+
+ pipecommon_semtake(&dev->d_bfsem);
+
+ /* Check if the decremented reference count would go to zero */
+
+ if (dev->d_refs > 1)
+ {
+ /* No.. then just decrement the reference count */
+
+ dev->d_refs--;
+
+ /* If opened for writing, decrement the count of writers on on the pipe instance */
+
+ if ((filep->f_oflags & O_WROK) != 0)
+ {
+ /* If there are no longer any writers on the pipe, then notify all of the
+ * waiting readers that they must return end-of-file.
+ */
+
+ if (--dev->d_nwriters <= 0)
+ {
+ while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0)
+ {
+ sem_post(&dev->d_rdsem);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Yes... deallocate the buffer */
+
+ kfree(dev->d_buffer);
+ dev->d_buffer = NULL;
+
+ /* And reset all counts and indices */
+
+ dev->d_wrndx = 0;
+ dev->d_rdndx = 0;
+ dev->d_refs = 0;
+ dev->d_nwriters = 0;
+ }
+
+ sem_post(&dev->d_bfsem);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: pipecommon_read
+ ****************************************************************************/
+
+ssize_t pipecommon_read(FAR struct file *filep, FAR char *buffer, size_t len)
+{
+ struct inode *inode = filep->f_inode;
+ struct pipe_dev_s *dev = inode->i_private;
+#ifdef CONFIG_DEV_PIPEDUMP
+ FAR uint8_t *start = (uint8_t*)buffer;
+#endif
+ ssize_t nread = 0;
+ int sval;
+ int ret;
+
+ /* Some sanity checking */
+#if CONFIG_DEBUG
+ if (!dev)
+ {
+ return -ENODEV;
+ }
+#endif
+
+ /* Make sure that we have exclusive access to the device structure */
+
+ if (sem_wait(&dev->d_bfsem) < 0)
+ {
+ return ERROR;
+ }
+
+ /* If the pipe is empty, then wait for something to be written to it */
+
+ while (dev->d_wrndx == dev->d_rdndx)
+ {
+ /* If O_NONBLOCK was set, then return EGAIN */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ sem_post(&dev->d_bfsem);
+ return -EAGAIN;
+ }
+
+ /* If there are no writers on the pipe, then return end of file */
+
+ if (dev->d_nwriters <= 0)
+ {
+ sem_post(&dev->d_bfsem);
+ return 0;
+ }
+
+ /* Otherwise, wait for something to be written to the pipe */
+
+ sched_lock();
+ sem_post(&dev->d_bfsem);
+ ret = sem_wait(&dev->d_rdsem);
+ sched_unlock();
+
+ if (ret < 0 || sem_wait(&dev->d_bfsem) < 0)
+ {
+ return ERROR;
+ }
+ }
+
+ /* Then return whatever is available in the pipe (which is at least one byte) */
+
+ nread = 0;
+ while (nread < len && dev->d_wrndx != dev->d_rdndx)
+ {
+ *buffer++ = dev->d_buffer[dev->d_rdndx];
+ if (++dev->d_rdndx >= CONFIG_DEV_PIPE_SIZE)
+ {
+ dev->d_rdndx = 0;
+ }
+ nread++;
+ }
+
+ /* Notify all waiting writers that bytes have been removed from the buffer */
+
+ while (sem_getvalue(&dev->d_wrsem, &sval) == 0 && sval < 0)
+ {
+ sem_post(&dev->d_wrsem);
+ }
+
+ /* Notify all poll/select waiters that they can write to the FIFO */
+
+ pipecommon_pollnotify(dev, POLLOUT);
+
+ sem_post(&dev->d_bfsem);
+ pipe_dumpbuffer("From PIPE:", start, nread);
+ return nread;
+}
+
+/****************************************************************************
+ * Name: pipecommon_write
+ ****************************************************************************/
+
+ssize_t pipecommon_write(FAR struct file *filep, FAR const char *buffer, size_t len)
+{
+ struct inode *inode = filep->f_inode;
+ struct pipe_dev_s *dev = inode->i_private;
+ ssize_t nwritten = 0;
+ ssize_t last;
+ int nxtwrndx;
+ int sval;
+
+ /* Some sanity checking */
+
+#if CONFIG_DEBUG
+ if (!dev)
+ {
+ return -ENODEV;
+ }
+#endif
+
+ pipe_dumpbuffer("To PIPE:", (uint8_t*)buffer, len);
+
+ /* At present, this method cannot be called from interrupt handlers. That is
+ * because it calls sem_wait (via pipecommon_semtake below) and sem_wait cannot
+ * be called from interrupt level. This actually happens fairly commonly
+ * IF dbg() is called from interrupt handlers and stdout is being redirected
+ * via a pipe. In that case, the debug output will try to go out the pipe
+ * (interrupt handlers should use the lldbg() APIs).
+ *
+ * On the other hand, it would be very valuable to be able to feed the pipe
+ * from an interrupt handler! TODO: Consider disabling interrupts instead
+ * of taking semaphores so that pipes can be written from interupt handlers
+ */
+
+ DEBUGASSERT(up_interrupt_context() == false)
+
+ /* Make sure that we have exclusive access to the device structure */
+
+ if (sem_wait(&dev->d_bfsem) < 0)
+ {
+ return ERROR;
+ }
+
+ /* Loop until all of the bytes have been written */
+
+ last = 0;
+ for (;;)
+ {
+ /* Calculate the write index AFTER the next byte is written */
+
+ nxtwrndx = dev->d_wrndx + 1;
+ if (nxtwrndx >= CONFIG_DEV_PIPE_SIZE)
+ {
+ nxtwrndx = 0;
+ }
+
+ /* Would the next write overflow the circular buffer? */
+
+ if (nxtwrndx != dev->d_rdndx)
+ {
+ /* No... copy the byte */
+
+ dev->d_buffer[dev->d_wrndx] = *buffer++;
+ dev->d_wrndx = nxtwrndx;
+
+ /* Is the write complete? */
+
+ if (++nwritten >= len)
+ {
+ /* Yes.. Notify all of the waiting readers that more data is available */
+
+ while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0)
+ {
+ sem_post(&dev->d_rdsem);
+ }
+
+ /* Notify all poll/select waiters that they can write to the FIFO */
+
+ pipecommon_pollnotify(dev, POLLIN);
+
+ /* Return the number of bytes written */
+
+ sem_post(&dev->d_bfsem);
+ return len;
+ }
+ }
+ else
+ {
+ /* There is not enough room for the next byte. Was anything written in this pass? */
+
+ if (last < nwritten)
+ {
+ /* Yes.. Notify all of the waiting readers that more data is available */
+
+ while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0)
+ {
+ sem_post(&dev->d_rdsem);
+ }
+ }
+ last = nwritten;
+
+ /* If O_NONBLOCK was set, then return partial bytes written or EGAIN */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ if (nwritten == 0)
+ {
+ nwritten = -EAGAIN;
+ }
+ sem_post(&dev->d_bfsem);
+ return nwritten;
+ }
+
+ /* There is more to be written.. wait for data to be removed from the pipe */
+
+ sched_lock();
+ sem_post(&dev->d_bfsem);
+ pipecommon_semtake(&dev->d_wrsem);
+ sched_unlock();
+ pipecommon_semtake(&dev->d_bfsem);
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: pipecommon_poll
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+int pipecommon_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct pipe_dev_s *dev = inode->i_private;
+ pollevent_t eventset;
+ pipe_ndx_t nbytes;
+ int ret = OK;
+ int i;
+
+ /* Some sanity checking */
+
+#if CONFIG_DEBUG
+ if (!dev || !fds)
+ {
+ return -ENODEV;
+ }
+#endif
+
+ /* Are we setting up the poll? Or tearing it down? */
+
+ pipecommon_semtake(&dev->d_bfsem);
+ if (setup)
+ {
+ /* This is a request to set up the poll. Find an available
+ * slot for the poll structure reference
+ */
+
+ for (i = 0; i < CONFIG_DEV_PIPE_NPOLLWAITERS; i++)
+ {
+ /* Find an available slot */
+
+ if (!dev->d_fds[i])
+ {
+ /* Bind the poll structure and this slot */
+
+ dev->d_fds[i] = fds;
+ fds->priv = &dev->d_fds[i];
+ break;
+ }
+ }
+
+ if (i >= CONFIG_DEV_PIPE_NPOLLWAITERS)
+ {
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto errout;
+ }
+
+ /* Should immediately notify on any of the requested events?
+ * First, determine how many bytes are in the buffer
+ */
+
+ if (dev->d_wrndx >= dev->d_rdndx)
+ {
+ nbytes = dev->d_wrndx - dev->d_rdndx;
+ }
+ else
+ {
+ nbytes = (CONFIG_DEV_PIPE_SIZE-1) + dev->d_wrndx - dev->d_rdndx;
+ }
+
+ /* Notify the POLLOUT event if the pipe is not full */
+
+ eventset = 0;
+ if (nbytes < (CONFIG_DEV_PIPE_SIZE-1))
+ {
+ eventset |= POLLOUT;
+ }
+
+ /* Notify the POLLIN event if the pipe is not empty */
+
+ if (nbytes > 0)
+ {
+ eventset |= POLLIN;
+ }
+
+ if (eventset)
+ {
+ pipecommon_pollnotify(dev, eventset);
+ }
+ }
+ else
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+
+#ifdef CONFIG_DEBUG
+ if (!slot)
+ {
+ ret = -EIO;
+ goto errout;
+ }
+#endif
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+errout:
+ sem_post(&dev->d_bfsem);
+ return ret;
+}
+#endif
+
+#endif /* CONFIG_DEV_PIPE_SIZE > 0 */
diff --git a/nuttx/drivers/pipes/pipe_common.h b/nuttx/drivers/pipes/pipe_common.h
new file mode 100644
index 000000000..44822e07f
--- /dev/null
+++ b/nuttx/drivers/pipes/pipe_common.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+ * drivers/pipe/pipe_common.h
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_PIPES_PIPE_COMMON_H
+#define __DRIVERS_PIPES_PIPE_COMMON_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <poll.h>
+
+#ifndef CONFIG_DEV_PIPE_SIZE
+# define CONFIG_DEV_PIPE_SIZE 1024
+#endif
+
+#if CONFIG_DEV_PIPE_SIZE > 0
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Maximum number of threads than can be waiting for POLL events */
+
+#ifndef CONFIG_DEV_PIPE_NPOLLWAITERS
+# define CONFIG_DEV_PIPE_NPOLLWAITERS 2
+#endif
+
+/* Maximum number of open's supported on pipe */
+
+#define CONFIG_DEV_PIPE_MAXUSER 255
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Make the buffer index as small as possible for the configured pipe size */
+
+#if CONFIG_DEV_PIPE_SIZE > 65535
+typedef uint32_t pipe_ndx_t; /* 32-bit index */
+#elif CONFIG_DEV_PIPE_SIZE > 255
+typedef uint16_t pipe_ndx_t; /* 16-bit index */
+#else
+typedef uint8_t pipe_ndx_t; /* 8-bit index */
+#endif
+
+/* This structure represents the state of one pipe. A reference to this
+ * structure is retained in the i_private field of the inode whenthe pipe/fifo
+ * device is registered.
+ */
+
+struct pipe_dev_s
+{
+ sem_t d_bfsem; /* Used to serialize access to d_buffer and indices */
+ sem_t d_rdsem; /* Empty buffer - Reader waits for data write */
+ sem_t d_wrsem; /* Full buffer - Writer waits for data read */
+ pipe_ndx_t d_wrndx; /* Index in d_buffer to save next byte written */
+ pipe_ndx_t d_rdndx; /* Index in d_buffer to return the next byte read */
+ uint8_t d_refs; /* References counts on pipe (limited to 255) */
+ uint8_t d_nwriters; /* Number of reference counts for write access */
+ uint8_t d_pipeno; /* Pipe minor number */
+ uint8_t *d_buffer; /* Buffer allocated when device opened */
+
+ /* The following is a list if poll structures of threads waiting for
+ * driver events. The 'struct pollfd' reference for each open is also
+ * retained in the f_priv field of the 'struct file'.
+ */
+
+#ifndef CONFIG_DISABLE_POLL
+ struct pollfd *d_fds[CONFIG_DEV_PIPE_NPOLLWAITERS];
+#endif
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+# define EXTERN extern "C"
+extern "C" {
+#else
+# define EXTERN extern
+#endif
+
+EXTERN FAR struct pipe_dev_s *pipecommon_allocdev(void);
+EXTERN void pipecommon_freedev(FAR struct pipe_dev_s *dev);
+EXTERN int pipecommon_open(FAR struct file *filep);
+EXTERN int pipecommon_close(FAR struct file *filep);
+EXTERN ssize_t pipecommon_read(FAR struct file *, FAR char *, size_t);
+EXTERN ssize_t pipecommon_write(FAR struct file *, FAR const char *, size_t);
+#ifndef CONFIG_DISABLE_POLL
+EXTERN int pipecommon_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup);
+#endif
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_DEV_PIPE_SIZE > 0 */
+#endif /* __DRIVERS_PIPES_PIPE_COMMON_H */
diff --git a/nuttx/drivers/power/Kconfig b/nuttx/drivers/power/Kconfig
new file mode 100644
index 000000000..ac76331b6
--- /dev/null
+++ b/nuttx/drivers/power/Kconfig
@@ -0,0 +1,23 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+config BATTERY
+ bool "Battery support"
+ default n
+
+config MAX1704X
+ bool "MAX1704X Battery charger support"
+ default n
+ select I2C
+ select I2C_MAX1704X
+ depends on BATTERY
+ ---help---
+ The MAX17040/MAX17041 are ultra-compact, low-cost, host-side fuel-gauge
+ systems for lithium-ion (Li+) batteries in handheld and portable equipment.
+ The MAX17040 is configured to operate with a single lithium cell and the
+ MAX17041 is configured for a dual-cell 2S pack.
+
+config I2C_MAX1704X
+ bool
+ default y if MAX1704X
diff --git a/nuttx/drivers/power/Make.defs b/nuttx/drivers/power/Make.defs
new file mode 100644
index 000000000..45c6aebc3
--- /dev/null
+++ b/nuttx/drivers/power/Make.defs
@@ -0,0 +1,84 @@
+############################################################################
+# drivers/power/Make.defs
+#
+# Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+POWER_DEPPATH =
+POWER_VPATH =
+POWER_CFLAGS =
+
+# Include power management sources
+
+ifeq ($(CONFIG_PM),y)
+
+CSRCS += pm_activity.c pm_changestate.c pm_checkstate.c pm_initialize.c pm_register.c pm_update.c
+
+# Include power management in the build
+
+POWER_DEPPATH := --dep-path power
+POWER_VPATH := :power
+POWER_CFLAGS := ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/power}
+
+endif
+
+# Add battery drivers
+
+ifeq ($(CONFIG_BATTERY),y)
+
+CSRCS += battery.c
+
+# Add I2C-based battery drivers
+
+ifeq ($(CONFIG_I2C),y)
+
+# Add the MAX1704x I2C-based battery driver
+
+ifeq ($(CONFIG_I2C_MAX1704X),y)
+CSRCS += max1704x.c
+endif
+
+endif
+
+# Include battery suport in the build
+
+POWER_DEPPATH := --dep-path power
+POWER_VPATH := :power
+POWER_CFLAGS := ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/power}
+
+endif
+
+# Include power management in the build
+
+DEPPATH += $(POWER_DEPPATH)
+VPATH += $(POWER_VPATH)
+CFLAGS += $(POWER_CFLAGS)
diff --git a/nuttx/drivers/power/battery.c b/nuttx/drivers/power/battery.c
new file mode 100644
index 000000000..698e5571b
--- /dev/null
+++ b/nuttx/drivers/power/battery.c
@@ -0,0 +1,254 @@
+/****************************************************************************
+ * drivers/power/battery.c
+ * Upper-half, character driver for batteries.
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/power/battery.h>
+
+/* This driver requires:
+ *
+ * CONFIG_BATTERY - Upper half battery driver support
+ */
+
+#if defined(CONFIG_BATTERY)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Character driver methods */
+
+static int bat_open(FAR struct file *filep);
+static int bat_close(FAR struct file *filep);
+static ssize_t bat_read(FAR struct file *, FAR char *, size_t nbytes);
+static ssize_t bat_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+static int bat_ioctl(FAR struct file *filep,int cmd,unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_batteryops =
+{
+ bat_open,
+ bat_close,
+ bat_read,
+ bat_write,
+ 0,
+ bat_ioctl
+#ifndef CONFIG_DISABLE_POLL
+ , 0
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Name: bat_open
+ *
+ * Description:
+ * This function is called whenever the battery device is opened.
+ *
+ ****************************************************************************/
+
+static int bat_open(FAR struct file *filep)
+{
+ return OK;
+}
+
+/****************************************************************************
+ * Name: bat_close
+ *
+ * Description:
+ * This routine is called when the battery device is closed.
+ *
+ ****************************************************************************/
+
+static int bat_close(FAR struct file *filep)
+{
+ return OK;
+}
+
+/****************************************************************************
+ * Name: bat_read
+ ****************************************************************************/
+
+static ssize_t bat_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ /* Return nothing read */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: bat_write
+ ****************************************************************************/
+
+static ssize_t bat_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen)
+{
+ /* Return nothing written */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: bat_ioctl
+ ****************************************************************************/
+
+static int bat_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct battery_dev_s *dev = inode->i_private;
+ int ret = -EINVAL;
+
+ /* Inforce mutually exclusive access to the battery driver */
+
+ ret = sem_wait(&dev->batsem);
+ if (ret < 0)
+ {
+ return -errno; /* Probably EINTR */
+ }
+
+ /* Procss the IOCTL command */
+
+ ret = -EINVAL; /* Assume a bad argument */
+ switch (cmd)
+ {
+ case BATIOC_STATE:
+ {
+ FAR int *ptr = (FAR int *)((uintptr_t)arg);
+ if (ptr)
+ {
+ ret = dev->ops->state(dev, ptr);
+ }
+ }
+ break;
+
+ case BATIOC_ONLINE:
+ {
+ FAR bool *ptr = (FAR bool *)((uintptr_t)arg);
+ if (ptr)
+ {
+ ret = dev->ops->online(dev, ptr);
+ }
+ }
+ break;
+
+ case BATIOC_VOLTAGE:
+ {
+ FAR b16_t *ptr = (FAR b16_t *)((uintptr_t)arg);
+ if (ptr)
+ {
+ ret = dev->ops->voltage(dev, ptr);
+ }
+ }
+ break;
+
+ case BATIOC_CAPACITY:
+ {
+ FAR b16_t *ptr = (FAR b16_t *)((uintptr_t)arg);
+ if (ptr)
+ {
+ ret = dev->ops->capacity(dev, ptr);
+ }
+ }
+ break;
+
+ default:
+ dbg("Unrecognized cmd: %d\n", cmd);
+ ret = -ENOTTY;
+ break;
+ }
+
+ sem_post(&dev->batsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: battery_register
+ *
+ * Description:
+ * Register a lower half battery driver with the common, upper-half
+ * battery driver.
+ *
+ * Input parameters:
+ * devpath - The location in the pseudo-filesystem to create the driver.
+ * Recommended standard is "/dev/bat0", "/dev/bat1", etc.
+ * dev - An instance of the battery state structure .
+ *
+ * Returned value:
+ * Zero on success or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int battery_register(FAR const char *devpath, FAR struct battery_dev_s *dev)
+{
+ int ret;
+
+ /* Register the character driver */
+
+ ret = register_driver(devpath, &g_batteryops, 0555, dev);
+ if (ret < 0)
+ {
+ dbg("Failed to register driver: %d\n", ret);
+ }
+ return ret;
+}
+#endif /* CONFIG_BATTERY */
diff --git a/nuttx/drivers/power/max1704x.c b/nuttx/drivers/power/max1704x.c
new file mode 100644
index 000000000..ec50515e6
--- /dev/null
+++ b/nuttx/drivers/power/max1704x.c
@@ -0,0 +1,564 @@
+/****************************************************************************
+ * drivers/power/max1704x.c
+ * Lower half driver for MAX1704x battery charger
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/* "The MAX17040/MAX17041 are ultra-compact, low-cost, host-side fuel-gauge
+ * systems for lithium-ion (Li+) batteries in handheld and portable equipment.
+ * The MAX17040 is configured to operate with a single lithium cell and the
+ * MAX17041 is configured for a dual-cell 2S pack.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/i2c.h>
+#include <nuttx/power/battery.h>
+
+/* This driver requires:
+ *
+ * CONFIG_BATTERY - Upper half battery driver support
+ * CONFIG_I2C - I2C support
+ * CONFIG_I2C_MAX1704X - And the driver must be explictly selected.
+ */
+
+#if defined(CONFIG_BATTERY) && defined(CONFIG_I2C) && defined(CONFIG_I2C_MAX1704X)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+/* CONFIG_I2C_MAX17040 or CONFIG_I2C_MAX17041 - The driver must know which
+ * chip is on the board in order to scale the voltage correctly.
+ */
+
+#if !defined(CONFIG_I2C_MAX17040) && !defined(CONFIG_I2C_MAX17041)
+# warning "Assuming CONFIG_I2C_MAX17040"
+# define CONFIG_I2C_MAX17040 1
+#endif
+
+/* MAX1704x Register Definitions ********************************************/
+/* "All host interaction with the MAX17040/MAX17041 is handled by writing to
+ * and reading from register locations. The MAX17040/MAX17041 have six 16-bit
+ * registers: SOC, VCELL, MODE, VERSION, RCOMP, and COMMAND. Register reads
+ * and writes are only valid if all 16 bits are transferred..."
+ */
+
+/* "VCELL Register. Battery voltage is measured at the CELL pin input with
+ * respect to GND over a 0 to 5.00V range for the MAX17040 and 0 to 10.00V
+ * for the MAX17041 with resolutions of 1.25mV and 2.50mV, respectively..."
+ */
+
+#define MAX1407X_VCELL_ADDR 0x02 /* Bits 4-15: Bits 0-11 of the battery voltage */
+
+/* VCELL conversion macros */
+
+#define MAX14700_VCELL_CONV 82 /* 0.00125 v * 65536 */
+#define MAX14070_VCELL(v) ((b16_t)(v) * MAX14700_VCELL_CONV)
+
+#define MAX14701_VCELL_CONV 163 /* 0.0025 v * 65536 */
+#define MAX14071_VCELL(v) ((b16_t)(v) * MAX14701_VCELL_CONV)
+
+#ifdef CONFIG_I2C_MAX17040
+# define MAX1407X_VCELL(v) MAX14070_VCELL(v)
+#else
+# define MAX1407X_VCELL(v) MAX14071_VCELL(v)
+#endif
+
+/* "SOC Register. The SOC register is a read-only register that displays the
+ * state of charge of the cell as calculated by the ModelGauge algorithm. The
+ * result is displayed as a percentage of the cell’s full capacity...
+ *
+ * "...Units of % can be directly determined by observing only the high byte
+ * of the SOC register. The low byte provides additional resolution in units
+ * 1/256%.
+ */
+
+#define MAX1407X_SOC_ADDR 0x04 /* Bits 0-15: Full SOC */
+
+/* SoC conversion macros */
+
+#define MAX1407X_SOC(s) ((b16_t)(s) << 8)
+#define MAX17040_SOC_FULL itob16(95) /* We say full if Soc >= 95% */
+
+/* "MODE Register.The MODE register allows the host processor to send special
+ * commands to the IC."
+ */
+
+#define MAX1407X_MODE_ADDR 0x06 /* Bits 0-15: 16-bit MODE */
+
+/* Supported modes */
+
+#define MAX1407X_MODE_QUICKSTART 0x4000
+
+/* "The VERSION register is a read-only register that contains a value
+ * indicating the production version of the MAX17040/MAX17041."
+ */
+
+#define MAX1407X_VERSION_ADDR 0x08 /* Bits 0-15: 16-bit VERSION */
+
+/* "RCOMP Register. RCOMP is a 16-bit value used to compensate the ModelGauge
+ * algorithm. RCOMP can be adjusted to optimize performance for different
+ * lithium chemistries or different operating temperatures... The factory-
+ * default value for RCOMP is 9700h."
+ */
+
+#define MAX1407X_RCOMP_ADDR 0x0c /* Bits 0-15: 16-bit RCOMP */
+
+/* "COMMAND Register. The COMMAND register allows the host processor to send
+ * special commands to the IC..."
+ */
+
+#define MAX1407X_COMMAND_ADDR 0xfe /* Bits 0-7: 16-bit COMMAND */
+
+/* Supported copmmands */
+
+#define MAX1407X_COMMAND_POR 0x5400
+
+/* Debug ********************************************************************/
+
+#ifdef CONFIG_DEBUG_MAX1704X
+# define batdbg dbg
+#else
+# ifdef CONFIG_CPP_HAVE_VARARGS
+# define batdbg(x...)
+# else
+# define batdbg (void)
+# endif
+#endif
+
+/****************************************************************************
+ * Private
+ ****************************************************************************/
+
+struct max1704x_dev_s
+{
+ /* The common part of the battery driver visible to the upper-half driver */
+
+ FAR const struct battery_operations_s *ops; /* Battery operations */
+ sem_t batsem; /* Enforce mutually exclusive access */
+
+ /* Data fields specific to the lower half MAX1704x driver follow */
+
+ FAR struct i2c_dev_s *i2c; /* I2C interface */
+ uint8_t addr; /* I2C address */
+ uint32_t frequency; /* I2C frequency */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* I2C support */
+
+static int max1704x_getreg16(FAR struct max1704x_dev_s *priv, uint8_t regaddr,
+ FAR uint16_t *regval);
+static int max1704x_putreg16(FAR struct max1704x_dev_s *priv, uint8_t regaddr,
+ uint16_t regval);
+
+static inline int max1704x_getvcell(FAR struct max1704x_dev_s *priv,
+ b16_t *vcell);
+static inline int max1704x_getsoc(FAR struct max1704x_dev_s *priv,
+ b16_t *soc);
+static inline int max1704x_setquikstart(FAR struct max1704x_dev_s *priv);
+static inline int max1704x_getversion(FAR struct max1704x_dev_s *priv,
+ uint16_t *version);
+static inline int max1704x_reset(FAR struct max1704x_dev_s *priv);
+
+/* Battery driver lower half methods */
+
+static int max1704x_state(struct battery_dev_s *dev, int *status);
+static int max1704x_online(struct battery_dev_s *dev, bool *status);
+static int max1704x_voltage(struct battery_dev_s *dev, b16_t *value);
+static int max1704x_capacity(struct battery_dev_s *dev, b16_t *value);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct battery_operations_s g_max1704xops =
+{
+ max1704x_state,
+ max1704x_online,
+ max1704x_voltage,
+ max1704x_capacity
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: max1704x_getreg16
+ *
+ * Description:
+ * Read a 16-bit value from a MAX1704x register pair.
+ *
+ * START <I2C write address> ACK <Reg address> ACK
+ * REPEATED-START <I2C read address> ACK Data0 ACK Data1 NO-ACK STOP
+ *
+ ****************************************************************************/
+
+static int max1704x_getreg16(FAR struct max1704x_dev_s *priv, uint8_t regaddr,
+ FAR uint16_t *regval)
+{
+ uint8_t buffer[2];
+ int ret;
+
+ /* Set the I2C address and address size */
+
+ I2C_SETADDRESS(priv->i2c, priv->addr, 7);
+
+ /* Write the register address */
+
+ ret = I2C_WRITE(priv->i2c, &regaddr, 1);
+ if (ret < 0)
+ {
+ batdbg("I2C_WRITE failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Restart and read 16-bits from the register */
+
+ ret = I2C_READ(priv->i2c, buffer, 2);
+ if (ret < 0)
+ {
+ batdbg("I2C_READ failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Return the 16-bit value */
+
+ return (uint16_t)buffer[0] << 8 | (uint16_t)buffer[1];
+ return OK;
+}
+
+/****************************************************************************
+ * Name: max1704x_putreg16
+ *
+ * Description:
+ * Write a 16-bit value to a MAX1704x register pair.
+ *
+ * START <I2C write address> ACK <Reg address> ACK Data0 ACK Data1 ACK STOP
+ *
+ ****************************************************************************/
+
+static int max1704x_putreg16(FAR struct max1704x_dev_s *priv, uint8_t regaddr,
+ uint16_t regval)
+{
+ uint8_t buffer[3];
+
+ batdbg("addr: %02x regval: %08x\n", regaddr, regval);
+
+ /* Set up a 3 byte message to send */
+
+ buffer[0] = regaddr;
+ buffer[1] = (uint8_t)(regval >> 8);
+ buffer[2] = (uint8_t)(regval & 0xff);
+
+ /* Set the I2C address and address size */
+
+ I2C_SETADDRESS(priv->i2c, priv->addr, 7);
+
+ /* Write the register address followed by the data (no RESTART) */
+
+ return I2C_WRITE(priv->i2c, buffer, 3);
+}
+
+/****************************************************************************
+ * Name: max1704x_getvcell
+ *
+ * Description:
+ * Read the VCELL register and scale the returned value
+ *
+ ****************************************************************************/
+
+static inline int max1704x_getvcell(FAR struct max1704x_dev_s *priv,
+ b16_t *vcell)
+{
+ uint16_t regval = 0;
+ int ret;
+
+ ret = max1704x_getreg16(priv, MAX1407X_VCELL_ADDR, &regval);
+ if (ret == OK)
+ {
+ *vcell = MAX1407X_VCELL(regval);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: max1704x_getsoc
+ *
+ * Description:
+ * Read the SOC register and scale the returned value
+ *
+ ****************************************************************************/
+
+static inline int max1704x_getsoc(FAR struct max1704x_dev_s *priv,
+ b16_t *soc)
+{
+ uint16_t regval = 0;
+ int ret;
+
+ ret = max1704x_getreg16(priv, MAX1407X_VCELL_ADDR, &regval);
+ if (ret == OK)
+ {
+ *soc = MAX1407X_SOC(regval);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: max1704x_setquikstart
+ *
+ * Description:
+ * Set Quickstart mode
+ *
+ ****************************************************************************/
+
+static inline int max1704x_setquikstart(FAR struct max1704x_dev_s *priv)
+{
+ return max1704x_putreg16(priv, MAX1407X_MODE_ADDR, MAX1407X_MODE_QUICKSTART);
+}
+
+/****************************************************************************
+ * Name: max1704x_getversion
+ *
+ * Description:
+ * Read the SOC register and scale the returned value
+ *
+ ****************************************************************************/
+
+static inline int max1704x_getversion(FAR struct max1704x_dev_s *priv,
+ uint16_t *version)
+{
+ return max1704x_getreg16(priv, MAX1407X_VCELL_ADDR, version);
+}
+
+/****************************************************************************
+ * Name: max1704x_setrcomp
+ *
+ * Description:
+ * Set Quickstart mode
+ *
+ ****************************************************************************/
+
+static inline int max1704x_setrcomp(FAR struct max1704x_dev_s *priv, uint16_t rcomp)
+{
+ return max1704x_putreg16(priv, MAX1407X_RCOMP_ADDR, rcomp);
+}
+
+/****************************************************************************
+ * Name: max1704x_reset
+ *
+ * Description:
+ * Reset the MAX1704x
+ *
+ ****************************************************************************/
+
+static inline int max1704x_reset(FAR struct max1704x_dev_s *priv)
+{
+ return max1704x_putreg16(priv, MAX1407X_COMMAND_ADDR, MAX1407X_COMMAND_POR);
+}
+
+/****************************************************************************
+ * Name: max1704x_state
+ *
+ * Description:
+ * Return the current battery state
+ *
+ ****************************************************************************/
+
+static int max1704x_state(struct battery_dev_s *dev, int *status)
+{
+ FAR struct max1704x_dev_s *priv = (FAR struct max1704x_dev_s *)dev;
+ b16_t soc = 0;
+ int ret;
+
+ /* Only a few of the possible battery states are supported by this driver:
+ *
+ * BATTERY_UNKNOWN - Returned on error conditions
+ * BATTERY_IDLE - This is what will usually be reported
+ * BATTERY_FULL - This will be reported if the SoC is greater than 95%
+ * BATTERY_CHARGING and BATTERY_DISCHARGING - I don't think this hardware
+ * knows anything about current (charging or dischargin).
+ */
+
+ ret = max1704x_getsoc(priv, &soc);
+ if (ret < 0)
+ {
+ *status = BATTERY_UNKNOWN;
+ return ret;
+ }
+
+ /* Is the battery fully charged? */
+
+ if (soc > MAX17040_SOC_FULL)
+ {
+ *status = BATTERY_FULL;
+ }
+ else
+ {
+ *status = BATTERY_IDLE;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: max1704x_online
+ *
+ * Description:
+ * Return true if the batter is online
+ *
+ ****************************************************************************/
+
+static int max1704x_online(struct battery_dev_s *dev, bool *status)
+{
+ /* There is no concept of online/offline in this driver */
+
+ *status = true;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: max1704x_voltage
+ *
+ * Description:
+ * Current battery voltage
+ *
+ ****************************************************************************/
+
+static int max1704x_voltage(struct battery_dev_s *dev, b16_t *value)
+{
+ FAR struct max1704x_dev_s *priv = (FAR struct max1704x_dev_s *)dev;
+ return max1704x_getvcell(priv, value);
+}
+
+/****************************************************************************
+ * Name: max1704x_capacity
+ *
+ * Description:
+ * Battery capacity
+ *
+ ****************************************************************************/
+
+static int max1704x_capacity(struct battery_dev_s *dev, b16_t *value)
+{
+ FAR struct max1704x_dev_s *priv = (FAR struct max1704x_dev_s *)dev;
+ return max1704x_getsoc(priv, value);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: max1704x_initialize
+ *
+ * Description:
+ * Initialize the MAX1704x battery driver and return an instance of the
+ * lower_half interface that may be used with battery_register();
+ *
+ * This driver requires:
+ *
+ * CONFIG_BATTERY - Upper half battery driver support
+ * CONFIG_I2C - I2C support
+ * CONFIG_I2C_MAX1704X - And the driver must be explictly selected.
+ * CONFIG_I2C_MAX17040 or CONFIG_I2C_MAX17041 - The driver must know which
+ * chip is on the board in order to scale the voltage correctly.
+ *
+ * Input Parameters:
+ * i2c - An instance of the I2C interface to use to communicate with the MAX1704x
+ * addr - The I2C address of the MAX1704x (Better be 0x36).
+ * frequency - The I2C frequency
+ *
+ * Returned Value:
+ * A pointer to the intialized lower-half driver instance. A NULL pointer
+ * is returned on a failure to initialize the MAX1704x lower half.
+ *
+ ****************************************************************************/
+
+FAR struct battery_dev_s *max1704x_initialize(FAR struct i2c_dev_s *i2c,
+ uint8_t addr, uint32_t frequency)
+{
+ FAR struct max1704x_dev_s *priv;
+#if 0
+ int ret;
+#endif
+
+ /* Initialize the MAX1704x device structure */
+
+ priv = (FAR struct max1704x_dev_s *)kzalloc(sizeof(struct max1704x_dev_s));
+ if (priv)
+ {
+ /* Initialize the MAX1704x device structure */
+
+ sem_init(&priv->batsem, 0, 1);
+ priv->ops = &g_max1704xops;
+ priv->i2c = i2c;
+ priv->addr = addr;
+ priv->frequency = frequency;
+
+ /* Set the I2C frequency (ignoring the returned, actual frequency) */
+
+ (void)I2C_SETFREQUENCY(i2c, priv->frequency);
+
+ /* Reset the MAX1704x (mostly just to make sure that we can talk to it) */
+
+#if 0
+ ret = max1704x_reset(priv);
+ if (ret < 0)
+ {
+ batdbg("Failed to reset the MAX1704x: %d\n", ret);
+ kfree(priv);
+ return NULL;
+ }
+#endif
+ }
+ return (FAR struct battery_dev_s *)priv;
+}
+
+#endif /* CONFIG_BATTERY && CONFIG_I2C && CONFIG_I2C_MAX1704X */
diff --git a/nuttx/drivers/power/pm_activity.c b/nuttx/drivers/power/pm_activity.c
new file mode 100644
index 000000000..d3c8a52e7
--- /dev/null
+++ b/nuttx/drivers/power/pm_activity.c
@@ -0,0 +1,166 @@
+/****************************************************************************
+ * drivers/power/pm_activity.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/power/pm.h>
+#include <nuttx/clock.h>
+#include <arch/irq.h>
+
+#include "pm_internal.h"
+
+#ifdef CONFIG_PM
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pm_activity
+ *
+ * Description:
+ * This function is called by a device driver to indicate that it is
+ * performing meaningful activities (non-idle). This increments an activity
+ * count and/or will restart a idle timer and prevent entering reduced
+ * power states.
+ *
+ * Input Parameters:
+ * priority - Activity priority, range 0-9. Larger values correspond to
+ * higher priorities. Higher priority activity can prevent the system
+ * from entering reduced power states for a longer period of time.
+ *
+ * As an example, a button press might be higher priority activity because
+ * it means that the user is actively interacting with the device.
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ * This function may be called from an interrupt handler (this is the ONLY
+ * PM function that may be called from an interrupt handler!).
+ *
+ ****************************************************************************/
+
+void pm_activity(int priority)
+{
+ uint32_t now;
+ uint32_t accum;
+ irqstate_t flags;
+
+ /* Just increment the activity count in the current time slice. The priority
+ * is simply the number of counts that are added.
+ */
+
+ if (priority > 0)
+ {
+ /* Add the priority to the accumulated counts in a critical section. */
+
+ flags = irqsave();
+ accum = (uint32_t)g_pmglobals.accum + priority;
+
+ /* Make sure that we do not overflow the underlying uint16_t representation */
+
+ if (accum > INT16_MAX)
+ {
+ accum = INT16_MAX;
+ }
+
+ /* Save the updated count */
+
+ g_pmglobals.accum = (int16_t)accum;
+
+ /* Check the elapsed time. In periods of low activity, time slicing is
+ * controlled by IDLE loop polling; in periods of higher activity, time
+ * slicing is controlled by driver activity. In either case, the duration
+ * of the time slice is only approximate; during times of heavy activity,
+ * time slices may be become longer and the activity level may be over-
+ * estimated.
+ */
+
+ now = clock_systimer();
+ if (now - g_pmglobals.stime >= TIME_SLICE_TICKS)
+ {
+ int16_t tmp;
+
+ /* Sample the count, reset the time and count, and assess the PM
+ * state. This is an atomic operation because interrupts are
+ * still disabled.
+ */
+
+ tmp = g_pmglobals.accum;
+ g_pmglobals.stime = now;
+ g_pmglobals.accum = 0;
+
+ /* Reassessing the PM state may require some computation. However,
+ * the work will actually be performed on a worker thread at a user-
+ * controlled priority.
+ */
+
+ (void)pm_update(accum);
+ }
+
+ irqrestore(flags);
+ }
+}
+
+#endif /* CONFIG_PM */ \ No newline at end of file
diff --git a/nuttx/drivers/power/pm_changestate.c b/nuttx/drivers/power/pm_changestate.c
new file mode 100644
index 000000000..f64760f55
--- /dev/null
+++ b/nuttx/drivers/power/pm_changestate.c
@@ -0,0 +1,227 @@
+/****************************************************************************
+ * drivers/power/pm_changestate.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/power/pm.h>
+#include <arch/irq.h>
+
+#include "pm_internal.h"
+
+#ifdef CONFIG_PM
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pm_prepall
+ *
+ * Description:
+ * Prepare every driver for the state change.
+ *
+ * Input Parameters:
+ * newstate - Identifies the new PM state
+ *
+ * Returned Value:
+ * 0 (OK) means that the callback function for all registered drivers
+ * returned OK (meaning that they accept the state change). Non-zero
+ * means that one of the drivers refused the state change. In this case,
+ * the system will revert to the preceding state.
+ *
+ * Assumptions:
+ * Interrupts are disabled.
+ *
+ ****************************************************************************/
+
+static int pm_prepall(enum pm_state_e newstate)
+{
+ FAR sq_entry_t *entry;
+ int ret = OK;
+
+ /* Visit each registered callback structure. */
+
+ for (entry = sq_peek(&g_pmglobals.registry);
+ entry && ret == OK;
+ entry = sq_next(entry))
+ {
+ /* Is the prepare callback supported? */
+
+ FAR struct pm_callback_s *cb = (FAR struct pm_callback_s *)entry;
+ if (cb->prepare)
+ {
+ /* Yes.. prepare the driver */
+
+ ret = cb->prepare(cb, newstate);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: pm_changeall
+ *
+ * Description:
+ * Inform all drivers of the state change.
+ *
+ * Input Parameters:
+ * newstate - Identifies the new PM state
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Interrupts are disabled.
+ *
+ ****************************************************************************/
+
+static inline void pm_changeall(enum pm_state_e newstate)
+{
+ FAR sq_entry_t *entry;
+
+ /* Visit each registered callback structure. */
+
+ for (entry = sq_peek(&g_pmglobals.registry); entry; entry = sq_next(entry))
+ {
+ /* Is the notification callback supported? */
+
+ FAR struct pm_callback_s *cb = (FAR struct pm_callback_s *)entry;
+ if (cb->notify)
+ {
+ /* Yes.. notify the driver */
+
+ cb->notify(cb, newstate);
+ }
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pm_changestate
+ *
+ * Description:
+ * This function is used by platform-specific power management logic. It
+ * will announce the power management power management state change to all
+ * drivers that have registered for power management event callbacks.
+ *
+ * Input Parameters:
+ * newstate - Identifies the new PM state
+ *
+ * Returned Value:
+ * 0 (OK) means that the callback function for all registered drivers
+ * returned OK (meaning that they accept the state change). Non-zero
+ * means that one of the drivers refused the state change. In this case,
+ * the system will revert to the preceding state.
+ *
+ * Assumptions:
+ * It is assumed that interrupts are disabled when this function is
+ * called. This function is probably called from the IDLE loop... the
+ * lowest priority task in the system. Changing driver power management
+ * states may result in renewed system activity and, as a result, can
+ * suspend the IDLE thread before it completes the entire state change
+ * unless interrupts are disabled throughout the state change.
+ *
+ ****************************************************************************/
+
+int pm_changestate(enum pm_state_e newstate)
+{
+ irqstate_t flags;
+ int ret;
+
+ /* Disable interrupts throught this operation... changing driver states
+ * could cause additional driver activity that might interfere with the
+ * state change. When the state change is complete, interrupts will be
+ * re-enabled.
+ */
+
+ flags = irqsave();
+
+ /* First, prepare the drivers for the state change. In this phase,
+ * drivers may refuse the state state change.
+ */
+
+ ret = pm_prepall(newstate);
+ if (ret != OK)
+ {
+ /* One or more drivers is not ready for this state change. Revert to
+ * the preceding state.
+ */
+
+ newstate = g_pmglobals.state;
+ (void)pm_prepall(newstate);
+ }
+
+ /* All drivers have agreed to the state change (or, one or more have
+ * disagreed and the state has been reverted). Set the new state.
+ */
+
+ pm_changeall(newstate);
+ g_pmglobals.state = newstate;
+
+ /* Restore the interrupt state */
+
+ irqrestore(flags);
+ return ret;
+}
+
+#endif /* CONFIG_PM */
diff --git a/nuttx/drivers/power/pm_checkstate.c b/nuttx/drivers/power/pm_checkstate.c
new file mode 100644
index 000000000..9b0e1045e
--- /dev/null
+++ b/nuttx/drivers/power/pm_checkstate.c
@@ -0,0 +1,161 @@
+/****************************************************************************
+ * drivers/power/pm_checkstate.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/power/pm.h>
+#include <nuttx/clock.h>
+#include <arch/irq.h>
+
+#include "pm_internal.h"
+
+#ifdef CONFIG_PM
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pm_checkstate
+ *
+ * Description:
+ * This function is called from the MCU-specific IDLE loop to monitor the
+ * the power management conditions. This function returns the "recommended"
+ * power management state based on the PM configuration and activity
+ * reported in the last sampling periods. The power management state is
+ * not automatically changed, however. The IDLE loop must call
+ * pm_changestate() in order to make the state change.
+ *
+ * These two steps are separated because the plaform-specific IDLE loop may
+ * have additional situational information that is not available to the
+ * the PM sub-system. For example, the IDLE loop may know that the
+ * battery charge level is very low and may force lower power states
+ * even if there is activity.
+ *
+ * NOTE: That these two steps are separated in time and, hence, the IDLE
+ * loop could be suspended for a long period of time between calling
+ * pm_checkstate() and pm_changestate(). The IDLE loop may need to make
+ * these calls atomic by either disabling interrupts until the state change
+ * is completed.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * The recommended power management state.
+ *
+ ****************************************************************************/
+
+enum pm_state_e pm_checkstate(void)
+{
+ uint32_t now;
+ irqstate_t flags;
+
+ /* Check for the end of the current time slice. This must be performed
+ * with interrupts disabled so that it does not conflict with the similar
+ * logic in pm_activity().
+ */
+
+ flags = irqsave();
+
+ /* Check the elapsed time. In periods of low activity, time slicing is
+ * controlled by IDLE loop polling; in periods of higher activity, time
+ * slicing is controlled by driver activity. In either case, the duration
+ * of the time slice is only approximate; during times of heavy activity,
+ * time slices may be become longer and the activity level may be over-
+ * estimated.
+ */
+
+ now = clock_systimer();
+ if (now - g_pmglobals.stime >= TIME_SLICE_TICKS)
+ {
+ int16_t accum;
+
+ /* Sample the count, reset the time and count, and assess the PM
+ * state. This is an atomic operation because interrupts are
+ * still disabled.
+ */
+
+ accum = g_pmglobals.accum;
+ g_pmglobals.stime = now;
+ g_pmglobals.accum = 0;
+
+ /* Reassessing the PM state may require some computation. However,
+ * the work will actually be performed on a worker thread at a user-
+ * controlled priority.
+ */
+
+ (void)pm_update(accum);
+ }
+ irqrestore(flags);
+
+ /* Return the recommended state. Assuming that we are called from the
+ * IDLE thread at the lowest priority level, any updates scheduled on the
+ * worker thread above should have already been peformed and the recommended
+ * state should be current:
+ */
+
+ return g_pmglobals.recommended;
+}
+
+#endif /* CONFIG_PM */
diff --git a/nuttx/drivers/power/pm_initialize.c b/nuttx/drivers/power/pm_initialize.c
new file mode 100644
index 000000000..9401fba9e
--- /dev/null
+++ b/nuttx/drivers/power/pm_initialize.c
@@ -0,0 +1,112 @@
+/****************************************************************************
+ * drivers/power/pm_initialize.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <semaphore.h>
+
+#include <nuttx/power/pm.h>
+
+#include "pm_internal.h"
+
+#ifdef CONFIG_PM
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* All PM global data: */
+
+struct pm_global_s g_pmglobals;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pm_initialize
+ *
+ * Description:
+ * This function is called by MCU-specific one-time at power on reset in
+ * order to initialize the power management capabilities. This function
+ * must be called *very* early in the intialization sequence *before* any
+ * other device drivers are initialize (since they may attempt to register
+ * with the power management subsystem).
+ *
+ * Input parameters:
+ * None.
+ *
+ * Returned value:
+ * None.
+ *
+ ****************************************************************************/
+
+void pm_initialize(void)
+{
+ /* Initialize the registry and the PM global data structures. The PM
+ * global data structure resides in .bss which is zeroed at boot time. So
+ * it is only required to initialize non-zero elements of the PM global
+ * data structure here.
+ */
+
+ sq_init(&g_pmglobals.registry);
+ sem_init(&g_pmglobals.regsem, 0, 1);
+}
+
+#endif /* CONFIG_PM */ \ No newline at end of file
diff --git a/nuttx/drivers/power/pm_internal.h b/nuttx/drivers/power/pm_internal.h
new file mode 100644
index 000000000..f98624f15
--- /dev/null
+++ b/nuttx/drivers/power/pm_internal.h
@@ -0,0 +1,210 @@
+/****************************************************************************
+ * drivers/power/pm_internal.h
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_POWER_PM_INTERNAL_H
+#define __DRIVERS_POWER_PM_INTERNAL_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <semaphore.h>
+#include <queue.h>
+
+#include <nuttx/power/pm.h>
+#include <nuttx/wqueue.h>
+
+#ifdef CONFIG_PM
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+# warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
+#endif
+
+/* Convert the time slice interval into system clock ticks.
+ *
+ * CONFIG_PM_SLICEMS provides the duration of one time slice in milliseconds.
+ * CLOCKS_PER_SEC provides the number of timer ticks in one second.
+ *
+ * slice ticks = (CONFIG_PM_SLICEMS msec / 1000 msec/sec) /
+ * (CLOCKS_PER_SEC ticks/sec)
+ */
+
+#define TIME_SLICE_TICKS ((CONFIG_PM_SLICEMS * CLOCKS_PER_SEC) / 1000)
+
+/* Function-like macros *****************************************************/
+/****************************************************************************
+ * Name: pm_lock
+ *
+ * Descripton:
+ * Lock the power management registry. NOTE: This function may return
+ * an error if a signal is received while what (errno == EINTR).
+ *
+ ****************************************************************************/
+
+#define pm_lock() sem_wait(&g_pmglobals.regsem);
+
+/****************************************************************************
+ * Name: pm_unlock
+ *
+ * Descripton:
+ * Unlock the power management registry.
+ *
+ ****************************************************************************/
+
+#define pm_unlock() sem_post(&g_pmglobals.regsem);
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+/* This structure encapsulates all of the global data used by the PM module */
+
+struct pm_global_s
+{
+ /* state - The current state (as determined by an explicit call to
+ * pm_changestate()
+ * recommended - The recommended state based on the PM algorithm in
+ * function pm_update().
+ * mndex - The index to the next slot in the memory[] array to use.
+ * mcnt - A tiny counter used only at start up. The actual
+ * algorithm cannot be applied until CONFIG_PM_MEMORY
+ * samples have been collected.
+ */
+
+ uint8_t state;
+ uint8_t recommended;
+ uint8_t mndx;
+ uint8_t mcnt;
+
+ /* accum - The accumulated counts in this time interval
+ * thrcnt - The number of below threshold counts seen.
+ */
+
+ int16_t accum;
+ uint16_t thrcnt;
+
+ /* This is the averaging "memory." The averaging algorithm is simply:
+ * Y = (An*X + SUM(Ai*Yi))/SUM(Aj), where i = 1..n-1 and j= 1..n, n is the
+ * length of the "memory", Ai is the weight applied to each value, and X is
+ * the current activity.
+ *
+ * CONFIG_PM_MEMORY provides the memory for the algorithm. Default: 2
+ * CONFIG_PM_COEFn provides weight for each sample. Default: 1
+ */
+
+#if CONFIG_PM_MEMORY > 1
+ int16_t memory[CONFIG_PM_MEMORY-1];
+#endif
+
+ /* stime - The time (in ticks) at the start of the current time slice */
+
+ uint32_t stime;
+
+ /* This semaphore manages mutually exclusive access to the power management
+ * registry. It must be initialized to the value 1.
+ */
+
+ sem_t regsem;
+
+ /* For work that has been deferred to the worker thread */
+
+ struct work_s work;
+
+ /* registry is a singly-linked list of registered power management
+ * callback structures. To ensure mutually exclusive access, this list
+ * must be locked by calling pm_lock() before it is accessed.
+ */
+
+ sq_queue_t registry;
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+# define EXTERN extern "C"
+extern "C"
+{
+#else
+# define EXTERN extern
+#endif
+
+/* All PM global data: */
+
+EXTERN struct pm_global_s g_pmglobals;
+
+/************************************************************************************
+ * Public Function Prototypes
+ ************************************************************************************/
+
+/****************************************************************************
+ * Name: pm_update
+ *
+ * Description:
+ * This internal function is called at the end of a time slice in order to
+ * update driver activity metrics and recommended states.
+ *
+ * Input Parameters:
+ * accum - The value of the activity accumulator at the end of the time
+ * slice.
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ * This function may be called from a driver, perhaps even at the interrupt
+ * level. It may also be called from the IDLE loop at the lowest possible
+ * priority level. To reconcile these various conditions, all work is
+ * performed on the worker thread at a user-selectable priority.
+ *
+ ****************************************************************************/
+
+EXTERN void pm_update(int16_t accum);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* CONFIG_PM */
+#endif /* #define __DRIVERS_POWER_PM_INTERNAL_H */
diff --git a/nuttx/drivers/power/pm_register.c b/nuttx/drivers/power/pm_register.c
new file mode 100644
index 000000000..19f94cb02
--- /dev/null
+++ b/nuttx/drivers/power/pm_register.c
@@ -0,0 +1,112 @@
+/****************************************************************************
+ * drivers/power/pm_register.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <queue.h>
+#include <assert.h>
+
+#include <nuttx/power/pm.h>
+
+#include "pm_internal.h"
+
+#ifdef CONFIG_PM
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pm_register
+ *
+ * Description:
+ * This function is called by a device driver in order to register to
+ * receive power management event callbacks.
+ *
+ * Input parameters:
+ * callbacks - An instance of struct pm_callback_s providing the driver
+ * callback functions.
+ *
+ * Returned value:
+ * Zero (OK) on success; otherwise a negater errno value is returned.
+ *
+ ****************************************************************************/
+
+int pm_register(FAR struct pm_callback_s *callbacks)
+{
+ int ret;
+
+ DEBUGASSERT(callbacks);
+
+ /* Add the new entry to the end of the list of registered callbacks */
+
+ ret = pm_lock();
+ if (ret == OK)
+ {
+ sq_addlast(&callbacks->entry, &g_pmglobals.registry);
+ pm_unlock();
+ }
+ return ret;
+}
+
+#endif /* CONFIG_PM */ \ No newline at end of file
diff --git a/nuttx/drivers/power/pm_update.c b/nuttx/drivers/power/pm_update.c
new file mode 100644
index 000000000..4b6b58c55
--- /dev/null
+++ b/nuttx/drivers/power/pm_update.c
@@ -0,0 +1,334 @@
+/****************************************************************************
+ * drivers/power/pm_update.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+
+#include <nuttx/power/pm.h>
+#include <nuttx/wqueue.h>
+
+#include "pm_internal.h"
+
+#ifdef CONFIG_PM
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+/* CONFIG_PM_MEMORY is the total number of time slices (including the current
+ * time slice. The histor or previous values is then CONFIG_PM_MEMORY-1.
+ */
+
+#if CONFIG_PM_MEMORY > 1
+static const int16_t g_pmcoeffs[CONFIG_PM_MEMORY-1] =
+{
+ CONFIG_PM_COEF1
+#if CONFIG_PM_MEMORY > 2
+ , CONFIG_PM_COEF2
+#endif
+#if CONFIG_PM_MEMORY > 3
+ , CONFIG_PM_COEF3
+#endif
+#if CONFIG_PM_MEMORY > 4
+ , CONFIG_PM_COEF4
+#endif
+#if CONFIG_PM_MEMORY > 5
+ , CONFIG_PM_COEF5
+#endif
+#if CONFIG_PM_MEMORY > 6
+# warning "This logic needs to be extended"
+#endif
+};
+#endif
+
+/* Threshold activity values to enter into the next lower power consumption
+ * state. Indexing is next state 0:IDLE, 1:STANDBY, 2:SLEEP.
+ */
+
+static const int16_t g_pmenterthresh[3] =
+{
+ CONFIG_PM_IDLEENTER_THRESH,
+ CONFIG_PM_STANDBYENTER_THRESH,
+ CONFIG_PM_SLEEPENTER_THRESH
+};
+
+/* Threshold activity values to leave the current low power consdumption
+ * state. Indexing is current state 0:IDLE, 1: STANDBY, 2: SLEEP.
+ */
+
+static const int16_t g_pmexitthresh[3] =
+{
+ CONFIG_PM_IDLEEXIT_THRESH,
+ CONFIG_PM_STANDBYEXIT_THRESH,
+ CONFIG_PM_SLEEPEXIT_THRESH
+};
+
+/* Threshold time slice count to enter the next low power consdumption
+ * state. Indexing is next state 0:IDLE, 1: STANDBY, 2: SLEEP.
+ */
+
+static const uint16_t g_pmcount[3] =
+{
+ CONFIG_PM_IDLEENTER_COUNT,
+ CONFIG_PM_STANDBYENTER_COUNT,
+ CONFIG_PM_SLEEPENTER_COUNT
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pm_worker
+ *
+ * Description:
+ * This worker function is queue at the end of a time slice in order to
+ * update driver activity metrics and recommended states.
+ *
+ * Input Parameters:
+ * arg - The value of the activity accumulator at the end of the time
+ * slice.
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ * This function runs on the worker thread.
+ *
+ ****************************************************************************/
+
+void pm_worker(FAR void *arg)
+{
+ int16_t accum = (int16_t)((intptr_t)arg);
+ int32_t Y;
+ int index;
+
+#if CONFIG_PM_MEMORY > 1
+ int32_t denom;
+ int i, j;
+
+ /* We won't bother to do anything until we have accumulated
+ * CONFIG_PM_MEMORY-1 samples.
+ */
+
+ if (g_pmglobals.mcnt < CONFIG_PM_MEMORY-1)
+ {
+ g_pmglobals.memory[g_pmglobals.mcnt] = accum;
+ g_pmglobals.mcnt++;
+ return;
+ }
+
+ /* The averaging algorithm is simply: Y = (An*X + SUM(Ai*Yi))/SUM(Aj), where
+ * i = 1..n-1 and j= 1..n, n is the length of the "memory", Ai is the
+ * weight applied to each value, and X is the current activity.
+ *
+ * CONFIG_PM_MEMORY provides the memory for the algorithm. Default: 2
+ * CONFIG_PM_COEFn provides weight for each sample. Default: 1
+ *
+ * First, calclate Y = An*X
+ */
+
+ Y = CONFIG_PM_COEFN * accum;
+ denom = CONFIG_PM_COEFN;
+
+ /* Then calculate Y += SUM(Ai*Yi), i = 1..n-1. The oldest sample will
+ * reside at g_pmglobals.mndx (and this is the value that we will overwrite
+ * with the new value).
+ */
+
+ for (i = 0, j = g_pmglobals.mndx; i < CONFIG_PM_MEMORY-1; i++, j++)
+ {
+ if (j >= CONFIG_PM_MEMORY-1)
+ {
+ j = 0;
+ }
+
+ Y += g_pmcoeffs[i] * g_pmglobals.memory[j];
+ denom += g_pmcoeffs[i];
+ }
+
+ /* Compute and save the new activity value */
+
+ Y /= denom;
+ g_pmglobals.memory[g_pmglobals.mndx] = Y;
+ g_pmglobals.mndx++;
+ if (g_pmglobals.mndx >= CONFIG_PM_MEMORY-1)
+ {
+ g_pmglobals.mndx = 0;
+ }
+
+#else
+
+ /* No smoothing */
+
+ Y = accum;
+
+#endif
+
+ /* First check if increased activity should cause us to return to the
+ * normal operating state. This would be unlikely for the lowest power
+ * consumption states because the CPU is probably asleep. However this
+ * probably does apply for the IDLE state.
+ */
+
+ if (g_pmglobals.state > PM_NORMAL)
+ {
+ /* Get the table index for the current state (which will be the
+ * current state minus one)
+ */
+
+ index = g_pmglobals.state - 1;
+
+ /* Has the threshold to return to normal power consumption state been
+ * exceeded?
+ */
+
+ if (Y > g_pmexitthresh[index])
+ {
+ /* Yes... reset the count and recommend the normal state. */
+
+ g_pmglobals.thrcnt = 0;
+ g_pmglobals.recommended = PM_NORMAL;
+ return;
+ }
+ }
+
+ /* Now, compare this new activity level to the thresholds and counts for
+ * the next lower power consumption state. If we are already in the SLEEP
+ * state, then there is nothing more to be done (in fact, I would be
+ * surprised to be executing!).
+ */
+
+ if (g_pmglobals.state < PM_SLEEP)
+ {
+ unsigned int nextstate;
+
+ /* Get the next state and the table index for the next state (which will
+ * be the current state)
+ */
+
+ index = g_pmglobals.state;
+ nextstate = g_pmglobals.state + 1;
+
+ /* Has the threshold to enter the next lower power consumption state
+ * been exceeded?
+ */
+
+ if (Y > g_pmenterthresh[index])
+ {
+ /* No... reset the count and recommend the current state */
+
+ g_pmglobals.thrcnt = 0;
+ g_pmglobals.recommended = g_pmglobals.state;
+ }
+
+ /* Yes.. have we already recommended this state? If so, do nothing */
+
+ else if (g_pmglobals.recommended < nextstate)
+ {
+ /* No.. increment the count. Has is passed the the count required
+ * for a state transition?
+ */
+
+ if (++g_pmglobals.thrcnt >= g_pmcount[index])
+ {
+ /* Yes, recommend the new state and set up for the next
+ * transition.
+ */
+
+ g_pmglobals.thrcnt = 0;
+ g_pmglobals.recommended = nextstate;
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pm_update
+ *
+ * Description:
+ * This internal function is called at the end of a time slice in order to
+ * update driver activity metrics and recommended states.
+ *
+ * Input Parameters:
+ * accum - The value of the activity accumulator at the end of the time
+ * slice.
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ * This function may be called from a driver, perhaps even at the interrupt
+ * level. It may also be called from the IDLE loop at the lowest possible
+ * priority level. To reconcile these various conditions, all work is
+ * performed on the worker thread at a user-selectable priority. This will
+ * also serialize all of the updates and eliminate any need for additional
+ * protection.
+ *
+ ****************************************************************************/
+
+void pm_update(int16_t accum)
+{
+ /* The work will be performed on the worker thread */
+
+ DEBUGASSERT(g_pmglobals.work.worker == NULL);
+ (void)work_queue(HPWORK, &g_pmglobals.work, pm_worker, (FAR void*)((intptr_t)accum), 0);
+}
+
+#endif /* CONFIG_PM */
diff --git a/nuttx/drivers/pwm.c b/nuttx/drivers/pwm.c
new file mode 100644
index 000000000..62fb4d2fb
--- /dev/null
+++ b/nuttx/drivers/pwm.c
@@ -0,0 +1,676 @@
+/****************************************************************************
+ * drivers/pwm.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Compilation Switches
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/pwm.h>
+
+#include <arch/irq.h>
+
+#ifdef CONFIG_PWM
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Debug ********************************************************************/
+/* Non-standard debug that may be enabled just for testing PWM */
+
+#ifdef CONFIG_DEBUG_PWM
+# define pwmdbg dbg
+# define pwmvdbg vdbg
+# define pwmlldbg lldbg
+# define pwmllvdbg llvdbg
+#else
+# define pwmdbg(x...)
+# define pwmvdbg(x...)
+# define pwmlldbg(x...)
+# define pwmllvdbg(x...)
+#endif
+
+/****************************************************************************
+ * Private Type Definitions
+ ****************************************************************************/
+
+/* This structure describes the state of the upper half driver */
+
+struct pwm_upperhalf_s
+{
+ uint8_t crefs; /* The number of times the device has been opened */
+ volatile bool started; /* True: pulsed output is being generated */
+#ifdef CONFIG_PWM_PULSECOUNT
+ volatile bool waiting; /* True: Caller is waiting for the pulse count to expire */
+#endif
+ sem_t exclsem; /* Supports mutual exclusion */
+#ifdef CONFIG_PWM_PULSECOUNT
+ sem_t waitsem; /* Used to wait for the pulse count to expire */
+#endif
+ struct pwm_info_s info; /* Pulsed output characteristics */
+ FAR struct pwm_lowerhalf_s *dev; /* lower-half state */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int pwm_open(FAR struct file *filep);
+static int pwm_close(FAR struct file *filep);
+static ssize_t pwm_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
+static ssize_t pwm_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+static int pwm_start(FAR struct pwm_upperhalf_s *upper, unsigned int oflags);
+static int pwm_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_pwmops =
+{
+ pwm_open, /* open */
+ pwm_close, /* close */
+ pwm_read, /* read */
+ pwm_write, /* write */
+ 0, /* seek */
+ pwm_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , 0 /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/************************************************************************************
+ * Name: pwm_open
+ *
+ * Description:
+ * This function is called whenever the PWM device is opened.
+ *
+ ************************************************************************************/
+
+static int pwm_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct pwm_upperhalf_s *upper = inode->i_private;
+ uint8_t tmp;
+ int ret;
+
+ pwmvdbg("crefs: %d\n", upper->crefs);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0)
+ {
+ ret = -errno;
+ goto errout;
+ }
+
+ /* Increment the count of references to the device. If this the first
+ * time that the driver has been opened for this device, then initialize
+ * the device.
+ */
+
+ tmp = upper->crefs + 1;
+ if (tmp == 0)
+ {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ goto errout_with_sem;
+ }
+
+ /* Check if this is the first time that the driver has been opened. */
+
+ if (tmp == 1)
+ {
+ FAR struct pwm_lowerhalf_s *lower = upper->dev;
+
+ /* Yes.. perform one time hardware initialization. */
+
+ DEBUGASSERT(lower->ops->setup != NULL);
+ pwmvdbg("calling setup\n");
+
+ ret = lower->ops->setup(lower);
+ if (ret < 0)
+ {
+ goto errout_with_sem;
+ }
+ }
+
+ /* Save the new open count on success */
+
+ upper->crefs = tmp;
+ ret = OK;
+
+errout_with_sem:
+ sem_post(&upper->exclsem);
+
+errout:
+ return ret;
+}
+
+/************************************************************************************
+ * Name: pwm_close
+ *
+ * Description:
+ * This function is called when the PWM device is closed.
+ *
+ ************************************************************************************/
+
+static int pwm_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct pwm_upperhalf_s *upper = inode->i_private;
+ int ret;
+
+ pwmvdbg("crefs: %d\n", upper->crefs);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0)
+ {
+ ret = -errno;
+ goto errout;
+ }
+
+ /* Decrement the references to the driver. If the reference count will
+ * decrement to 0, then uninitialize the driver.
+ */
+
+ if (upper->crefs > 1)
+ {
+ upper->crefs--;
+ }
+ else
+ {
+ FAR struct pwm_lowerhalf_s *lower = upper->dev;
+
+ /* There are no more references to the port */
+
+ upper->crefs = 0;
+
+ /* Disable the PWM device */
+
+ DEBUGASSERT(lower->ops->shutdown != NULL);
+ pwmvdbg("calling shutdown: %d\n");
+
+ lower->ops->shutdown(lower);
+ }
+ ret = OK;
+
+//errout_with_sem:
+ sem_post(&upper->exclsem);
+
+errout:
+ return ret;
+}
+
+/************************************************************************************
+ * Name: pwm_read
+ *
+ * Description:
+ * A dummy read method. This is provided only to satsify the VFS layer.
+ *
+ ************************************************************************************/
+
+static ssize_t pwm_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ /* Return zero -- usually meaning end-of-file */
+
+ return 0;
+}
+
+/************************************************************************************
+ * Name: pwm_write
+ *
+ * Description:
+ * A dummy write method. This is provided only to satsify the VFS layer.
+ *
+ ************************************************************************************/
+
+static ssize_t pwm_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+{
+ return 0;
+}
+
+/************************************************************************************
+ * Name: pwm_start
+ *
+ * Description:
+ * Handle the PWMIOC_START ioctl command
+ *
+ ************************************************************************************/
+
+#ifdef CONFIG_PWM_PULSECOUNT
+static int pwm_start(FAR struct pwm_upperhalf_s *upper, unsigned int oflags)
+{
+ FAR struct pwm_lowerhalf_s *lower = upper->dev;
+ irqstate_t flags;
+ int ret = OK;
+
+ DEBUGASSERT(upper != NULL && lower->ops->start != NULL);
+
+ /* Verify that the PWM is not already running */
+
+ if (!upper->started)
+ {
+ /* Disable interrupts to avoid race conditions */
+
+ flags = irqsave();
+
+ /* Indicate that if will be waiting for the pulse count to complete.
+ * Note that we will only wait if a non-zero pulse count is specified
+ * and if the PWM driver was opened in normal, blocking mode. Also
+ * assume for now that the pulse train will be successfully started.
+ *
+ * We do these things before starting the PWM to avoid race conditions.
+ */
+
+ upper->waiting = (upper->info.count > 0) && ((oflags & O_NONBLOCK) == 0);
+ upper->started = true;
+
+ /* Invoke the bottom half method to start the pulse train */
+
+ ret = lower->ops->start(lower, &upper->info, upper);
+
+ /* A return value of zero means that the pulse train was started
+ * successfully.
+ */
+
+ if (ret == OK)
+ {
+ /* Should we wait for the pulse output to complete? Loop in
+ * in case the wakeup form sem_wait() is a false alarm.
+ */
+
+ while (upper->waiting)
+ {
+ /* Wait until we are awakened by pwm_expired(). When
+ * pwm_expired is called, it will post the waitsem and
+ * clear the waiting flag.
+ */
+
+ int tmp = sem_wait(&upper->waitsem);
+ DEBUGASSERT(tmp == OK || errno == EINTR);
+ }
+ }
+ else
+ {
+ /* Looks like we won't be waiting after all */
+
+ pwmvdbg("start failed: %d\n", ret);
+ upper->started = false;
+ upper->waiting = false;
+ }
+
+ irqrestore(flags);
+ }
+
+ return ret;
+}
+#else
+static int pwm_start(FAR struct pwm_upperhalf_s *upper, unsigned int oflags)
+{
+ FAR struct pwm_lowerhalf_s *lower = upper->dev;
+ int ret = OK;
+
+ DEBUGASSERT(upper != NULL && lower->ops->start != NULL);
+
+ /* Verify that the PWM is not already running */
+
+ if (!upper->started)
+ {
+ /* Invoke the bottom half method to start the pulse train */
+
+ ret = lower->ops->start(lower, &upper->info);
+
+ /* A return value of zero means that the pulse train was started
+ * successfully.
+ */
+
+ if (ret == OK)
+ {
+ /* Indicate that the pulse train has started */
+
+ upper->started = true;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+/************************************************************************************
+ * Name: pwm_ioctl
+ *
+ * Description:
+ * The standard ioctl method. This is where ALL of the PWM work is done.
+ *
+ ************************************************************************************/
+
+static int pwm_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct pwm_upperhalf_s *upper = inode->i_private;
+ FAR struct pwm_lowerhalf_s *lower = upper->dev;
+ int ret;
+
+ pwmvdbg("cmd: %d arg: %ld\n", cmd, arg);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Handle built-in ioctl commands */
+
+ switch (cmd)
+ {
+ /* PWMIOC_SETCHARACTERISTICS - Set the characteristics of the next pulsed
+ * output. This command will neither start nor stop the pulsed output.
+ * It will either setup the configuration that will be used when the
+ * output is started; or it will change the characteristics of the pulsed
+ * output on the fly if the timer is already started.
+ *
+ * ioctl argument: A read-only reference to struct pwm_info_s that provides
+ * the characteristics of the pulsed output.
+ */
+
+ case PWMIOC_SETCHARACTERISTICS:
+ {
+ FAR const struct pwm_info_s *info = (FAR const struct pwm_info_s*)((uintptr_t)arg);
+ DEBUGASSERT(info != NULL && lower->ops->start != NULL);
+
+#ifdef CONFIG_PWM_PULSECOUNT
+ pwmvdbg("PWMIOC_SETCHARACTERISTICS frequency: %d duty: %08x count: %d started: %d\n",
+ info->frequency, info->duty, info->count, upper->started);
+#else
+ pwmvdbg("PWMIOC_SETCHARACTERISTICS frequency: %d duty: %08x started: %d\n",
+ info->frequency, info->duty, upper->started);
+#endif
+
+ /* Save the pulse train characteristics */
+
+ memcpy(&upper->info, info, sizeof(struct pwm_info_s));
+
+ /* If PWM is already running, then re-start it with the new characteristics */
+
+ if (upper->started)
+ {
+#ifdef CONFIG_PWM_PULSECOUNT
+ ret = lower->ops->start(lower, &upper->info, upper);
+#else
+ ret = lower->ops->start(lower, &upper->info);
+#endif
+ }
+ }
+ break;
+
+ /* PWMIOC_GETCHARACTERISTICS - Get the currently selected characteristics of
+ * the pulsed output (independent of whether the output is start or stopped).
+ *
+ * ioctl argument: A reference to struct pwm_info_s to recevie the
+ * characteristics of the pulsed output.
+ */
+
+ case PWMIOC_GETCHARACTERISTICS:
+ {
+ FAR struct pwm_info_s *info = (FAR struct pwm_info_s*)((uintptr_t)arg);
+ DEBUGASSERT(info != NULL);
+
+ memcpy(info, &upper->info, sizeof(struct pwm_info_s));
+
+#ifdef CONFIG_PWM_PULSECOUNT
+ pwmvdbg("PWMIOC_GETCHARACTERISTICS frequency: %d duty: %08x count: %d\n",
+ info->frequency, info->duty, info->count);
+#else
+ pwmvdbg("PWMIOC_GETCHARACTERISTICS frequency: %d duty: %08x\n",
+ info->frequency, info->duty);
+#endif
+ }
+ break;
+
+ /* PWMIOC_START - Start the pulsed output. The PWMIOC_SETCHARACTERISTICS
+ * command must have previously been sent.
+ *
+ * ioctl argument: None
+ */
+
+ case PWMIOC_START:
+ {
+#ifdef CONFIG_PWM_PULSECOUNT
+ pwmvdbg("PWMIOC_START frequency: %d duty: %08x count: %d started: %d\n",
+ upper->info.frequency, upper->info.duty, upper->info.count,
+ upper->started);
+#else
+ pwmvdbg("PWMIOC_START frequency: %d duty: %08x started: %d\n",
+ upper->info.frequency, upper->info.duty, upper->started);
+#endif
+ DEBUGASSERT(lower->ops->start != NULL);
+
+ /* Start the pulse train */
+
+ ret = pwm_start(upper, filep->f_oflags);
+ }
+ break;
+
+ /* PWMIOC_STOP - Stop the pulsed output.
+ *
+ * ioctl argument: None
+ */
+
+ case PWMIOC_STOP:
+ {
+ pwmvdbg("PWMIOC_STOP: started: %d\n", upper->started);
+ DEBUGASSERT(lower->ops->stop != NULL);
+
+ if (upper->started)
+ {
+ ret = lower->ops->stop(lower);
+ upper->started = false;
+#ifdef CONFIG_PWM_PULSECOUNT
+ if (upper->waiting)
+ {
+ upper->waiting = FALSE;
+ }
+#endif
+ }
+ }
+ break;
+
+ /* Any unrecognized IOCTL commands might be platform-specific ioctl commands */
+
+ default:
+ {
+ pwmvdbg("Forwarding unrecognized cmd: %d arg: %ld\n", cmd, arg);
+ DEBUGASSERT(lower->ops->ioctl != NULL);
+ ret = lower->ops->ioctl(lower, cmd, arg);
+ }
+ break;
+ }
+
+ sem_post(&upper->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pwm_register
+ *
+ * Description:
+ * This function binds an instance of a "lower half" timer driver with the
+ * "upper half" PWM device and registers that device so that can be used
+ * by application code.
+ *
+ * When this function is called, the "lower half" driver should be in the
+ * reset state (as if the shutdown() method had already been called).
+ *
+ * Input parameters:
+ * path - The full path to the driver to be registers in the NuttX pseudo-
+ * filesystem. The recommended convention is to name all PWM drivers
+ * as "/dev/pwm0", "/dev/pwm1", etc. where the driver path differs only
+ * in the "minor" number at the end of the device name.
+ * dev - A pointer to an instance of lower half timer driver. This instance
+ * is bound to the PWM driver and must persists as long as the driver
+ * persists.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int pwm_register(FAR const char *path, FAR struct pwm_lowerhalf_s *dev)
+{
+ FAR struct pwm_upperhalf_s *upper;
+
+ /* Allocate the upper-half data structure */
+
+ upper = (FAR struct pwm_upperhalf_s *)zalloc(sizeof(struct pwm_upperhalf_s));
+ if (!upper)
+ {
+ pwmdbg("Allocation failed\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the PWM device structure (it was already zeroed by zalloc()) */
+
+ sem_init(&upper->exclsem, 0, 1);
+#ifdef CONFIG_PWM_PULSECOUNT
+ sem_init(&upper->waitsem, 0, 0);
+#endif
+ upper->dev = dev;
+
+ /* Register the PWM device */
+
+ pwmvdbg("Registering %s\n", path);
+ return register_driver(path, &g_pwmops, 0666, upper);
+}
+
+/****************************************************************************
+ * Name: pwm_expired
+ *
+ * Description:
+ * If CONFIG_PWM_PULSECOUNT is defined and the pulse count was configured
+ * to a non-zero value, then the "upper half" driver will wait for the
+ * pulse count to expire. The sequence of expected events is as follows:
+ *
+ * 1. The upper half driver calls the start method, providing the lower
+ * half driver with the pulse train characteristics. If a fixed
+ * number of pulses is required, the 'count' value will be nonzero.
+ * 2. The lower half driver's start() methoc must verify that it can
+ * support the request pulse train (frequency, duty, AND pulse count).
+ * It it cannot, it should return an error. If the pulse count is
+ * non-zero, it should set up the hardware for that number of pulses
+ * and return success. NOTE: That is CONFIG_PWM_PULSECOUNT is
+ * defined, the start() method receives an additional parameter
+ * that must be used in this callback.
+ * 3. When the start() method returns success, the upper half driver
+ * will "sleep" until the pwm_expired method is called.
+ * 4. When the lower half detects that the pulse count has expired
+ * (probably through an interrupt), it must call the pwm_expired
+ * interface using the handle that was previously passed to the
+ * start() method
+ *
+ * Input parameters:
+ * handle - This is the handle that was provided to the lower-half
+ * start() method.
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * This function may be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PWM_PULSECOUNT
+void pwm_expired(FAR void *handle)
+{
+ FAR struct pwm_upperhalf_s *upper = (FAR struct pwm_upperhalf_s *)handle;
+
+ pwmllvdbg("started: %d waiting: %d\n", upper->started, upper->waiting);
+
+ /* Make sure that the PWM is started */
+
+ if (upper->started)
+ {
+ /* Is there a thread waiting for the pulse train to complete? */
+
+ if (upper->waiting)
+ {
+ /* Yes.. clear the waiting flag and awakened the waiting thread */
+
+ upper->waiting = false;
+ sem_post(&upper->waitsem);
+ }
+
+ /* The PWM is now stopped */
+
+ upper->started = false;
+ }
+}
+#endif
+
+#endif /* CONFIG_PWM */
diff --git a/nuttx/drivers/ramdisk.c b/nuttx/drivers/ramdisk.c
new file mode 100644
index 000000000..91912b25c
--- /dev/null
+++ b/nuttx/drivers/ramdisk.c
@@ -0,0 +1,342 @@
+/****************************************************************************
+ * drivers/ramdisk.c
+ *
+ * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/ramdisk.h>
+
+/****************************************************************************
+ * Private Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct rd_struct_s
+{
+ uint32_t rd_nsectors; /* Number of sectors on device */
+ uint16_t rd_sectsize; /* The size of one sector */
+#ifdef CONFIG_FS_WRITABLE
+ bool rd_writeenabled; /* true: can write to ram disk */
+ uint8_t *rd_buffer; /* RAM disk backup memory */
+#else
+ const uint8_t *rd_buffer; /* ROM disk backup memory */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int rd_open(FAR struct inode *inode);
+static int rd_close(FAR struct inode *inode);
+static ssize_t rd_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors);
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t rd_write(FAR struct inode *inode, const unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors);
+#endif
+static int rd_geometry(FAR struct inode *inode, struct geometry *geometry);
+static int rd_ioctl(FAR struct inode *inode, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct block_operations g_bops =
+{
+ rd_open, /* open */
+ rd_close, /* close */
+ rd_read, /* read */
+#ifdef CONFIG_FS_WRITABLE
+ rd_write, /* write */
+#else
+ NULL, /* write */
+#endif
+ rd_geometry, /* geometry */
+ rd_ioctl /* ioctl */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rd_open
+ *
+ * Description: Open the block device
+ *
+ ****************************************************************************/
+
+static int rd_open(FAR struct inode *inode)
+{
+ fvdbg("Entry\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: rd_closel
+ *
+ * Description: close the block device
+ *
+ ****************************************************************************/
+
+static int rd_close(FAR struct inode *inode)
+{
+ fvdbg("Entry\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: rd_read
+ *
+ * Description: Read the specified numer of sectors
+ *
+ ****************************************************************************/
+
+static ssize_t rd_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
+{
+ struct rd_struct_s *dev;
+
+ DEBUGASSERT(inode && inode->i_private);
+ dev = (struct rd_struct_s *)inode->i_private;
+
+ fvdbg("sector: %d nsectors: %d sectorsize: %d\n",
+ start_sector, dev->rd_sectsize, nsectors);
+
+ if (start_sector < dev->rd_nsectors &&
+ start_sector + nsectors <= dev->rd_nsectors)
+ {
+ fvdbg("Transfer %d bytes from %p\n",
+ nsectors * dev->rd_sectsize,
+ &dev->rd_buffer[start_sector * dev->rd_sectsize]);
+
+ memcpy(buffer,
+ &dev->rd_buffer[start_sector * dev->rd_sectsize],
+ nsectors * dev->rd_sectsize);
+ return nsectors;
+ }
+
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: rd_write
+ *
+ * Description: Write the specified number of sectors
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t rd_write(FAR struct inode *inode, const unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
+{
+ struct rd_struct_s *dev;
+
+ DEBUGASSERT(inode && inode->i_private);
+ dev = (struct rd_struct_s *)inode->i_private;
+
+ fvdbg("sector: %d nsectors: %d sectorsize: %d\n",
+ start_sector, dev->rd_sectsize, nsectors);
+
+ if (!dev->rd_writeenabled)
+ {
+ return -EACCES;
+ }
+ else if (start_sector < dev->rd_nsectors &&
+ start_sector + nsectors <= dev->rd_nsectors)
+ {
+ fvdbg("Transfer %d bytes from %p\n",
+ nsectors * dev->rd_sectsize,
+ &dev->rd_buffer[start_sector * dev->rd_sectsize]);
+
+ memcpy(&dev->rd_buffer[start_sector * dev->rd_sectsize],
+ buffer,
+ nsectors * dev->rd_sectsize);
+ return nsectors;
+ }
+
+ return -EFBIG;
+}
+#endif
+
+/****************************************************************************
+ * Name: rd_geometry
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int rd_geometry(FAR struct inode *inode, struct geometry *geometry)
+{
+ struct rd_struct_s *dev;
+
+ fvdbg("Entry\n");
+
+ DEBUGASSERT(inode);
+ if (geometry)
+ {
+ dev = (struct rd_struct_s *)inode->i_private;
+ geometry->geo_available = true;
+ geometry->geo_mediachanged = false;
+#ifdef CONFIG_FS_WRITABLE
+ geometry->geo_writeenabled = dev->rd_writeenabled;
+#else
+ geometry->geo_writeenabled = false;
+#endif
+ geometry->geo_nsectors = dev->rd_nsectors;
+ geometry->geo_sectorsize = dev->rd_sectsize;
+
+ fvdbg("available: true mediachanged: false writeenabled: %s\n",
+ geometry->geo_writeenabled ? "true" : "false");
+ fvdbg("nsectors: %d sectorsize: %d\n",
+ geometry->geo_nsectors, geometry->geo_sectorsize);
+
+ return OK;
+ }
+
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: rd_ioctl
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int rd_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
+{
+ struct rd_struct_s *dev ;
+ void **ppv = (void**)((uintptr_t)arg);
+
+ fvdbg("Entry\n");
+
+ /* Only one ioctl command is supported */
+
+ DEBUGASSERT(inode && inode->i_private);
+ if (cmd == BIOC_XIPBASE && ppv)
+ {
+ dev = (struct rd_struct_s *)inode->i_private;
+ *ppv = (void*)dev->rd_buffer;
+
+ fvdbg("ppv: %p\n", *ppv);
+ return OK;
+ }
+
+ return -ENOTTY;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ramdisk_register
+ *
+ * Description: Register the a ramdisk
+
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+int ramdisk_register(int minor, uint8_t *buffer, uint32_t nsectors,
+ uint16_t sectsize, bool writeenabled)
+#else
+int romdisk_register(int minor, uint8_t *buffer, uint32_t nsectors,
+ uint16_t sectsize)
+#endif
+{
+ struct rd_struct_s *dev;
+ char devname[16];
+ int ret = -ENOMEM;
+
+ fvdbg("buffer: %p nsectors: %d sectsize: %d\n", buffer, nsectors, sectsize);
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (minor < 0 || minor > 255 || !buffer || !nsectors || !sectsize)
+ {
+ return -EINVAL;
+ }
+#endif
+
+ /* Allocate a ramdisk device structure */
+
+ dev = (struct rd_struct_s *)kmalloc(sizeof(struct rd_struct_s));
+ if (dev)
+ {
+ /* Initialize the ramdisk device structure */
+
+ dev->rd_nsectors = nsectors; /* Number of sectors on device */
+ dev->rd_sectsize = sectsize; /* The size of one sector */
+#ifdef CONFIG_FS_WRITABLE
+ dev->rd_writeenabled = writeenabled; /* true: can write to ram disk */
+#endif
+ dev->rd_buffer = buffer; /* RAM disk backup memory */
+
+ /* Create a ramdisk device name */
+
+ snprintf(devname, 16, "/dev/ram%d", minor);
+
+ /* Inode private data is a reference to the ramdisk device stgructure */
+
+ ret = register_blockdriver(devname, &g_bops, 0, dev);
+ if (ret < 0)
+ {
+ fdbg("register_blockdriver failed: %d\n", -ret);
+ kfree(dev);
+ }
+ }
+ return ret;
+}
diff --git a/nuttx/drivers/rwbuffer.c b/nuttx/drivers/rwbuffer.c
new file mode 100644
index 000000000..076ebc781
--- /dev/null
+++ b/nuttx/drivers/rwbuffer.c
@@ -0,0 +1,682 @@
+/****************************************************************************
+ * drivers/rwbuffer.c
+ *
+ * Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/rwbuffer.h>
+
+#if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD)
+
+/****************************************************************************
+ * Preprocessor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+# error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
+#endif
+
+#ifndef CONFIG_FS_WRDELAY
+# define CONFIG_FS_WRDELAY 350
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rwb_semtake
+ ****************************************************************************/
+
+static void rwb_semtake(sem_t *sem)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(sem) != 0)
+ {
+ /* The only case that an error should occr here is if
+ * the wait was awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: rwb_semgive
+ ****************************************************************************/
+
+#define rwb_semgive(s) sem_post(s)
+
+/****************************************************************************
+ * Name: rwb_overlap
+ ****************************************************************************/
+
+static inline bool rwb_overlap(off_t blockstart1, size_t nblocks1,
+ off_t blockstart2, size_t nblocks2)
+{
+ off_t blockend1 = blockstart1 + nblocks1;
+ off_t blockend2 = blockstart2 + nblocks2;
+
+ /* If the buffer 1 is wholly outside of buffer 2, return false */
+
+ if ((blockend1 < blockstart2) || /* Wholly "below" */
+ (blockstart1 > blockend2)) /* Wholly "above" */
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+/****************************************************************************
+ * Name: rwb_resetwrbuffer
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITEBUFFER
+static inline void rwb_resetwrbuffer(struct rwbuffer_s *rwb)
+{
+ /* We assume that the caller holds the wrsem */
+
+ rwb->wrnblocks = 0;
+ rwb->wrblockstart = (off_t)-1;
+ rwb->wrexpectedblock = (off_t)-1;
+}
+#endif
+
+/****************************************************************************
+ * Name: rwb_wrflush
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITEBUFFER
+static void rwb_wrflush(struct rwbuffer_s *rwb)
+{
+ int ret;
+
+ fvdbg("Timeout!\n");
+
+ rwb_semtake(&rwb->wrsem);
+ if (rwb->wrnblocks)
+ {
+ fvdbg("Flushing: blockstart=0x%08lx nblocks=%d from buffer=%p\n",
+ (long)rwb->wrblockstart, rwb->wrnblocks, rwb->wrbuffer);
+
+ /* Flush cache. On success, the flush method will return the number
+ * of blocks written. Anything other than the number requested is
+ * an error.
+ */
+
+ ret = rwb->wrflush(rwb->dev, rwb->wrbuffer, rwb->wrblockstart, rwb->wrnblocks);
+ if (ret != rwb->wrnblocks)
+ {
+ fdbg("ERROR: Error flushing write buffer: %d\n", ret);
+ }
+
+ rwb_resetwrbuffer(rwb);
+ }
+
+ rwb_semgive(&rwb->wrsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: rwb_wrtimeout
+ ****************************************************************************/
+
+static void rwb_wrtimeout(FAR void *arg)
+{
+ /* The following assumes that the size of a pointer is 4-bytes or less */
+
+ FAR struct rwbuffer_s *rwb = (struct rwbuffer_s *)arg;
+ DEBUGASSERT(rwb != NULL);
+
+ /* If a timeout elpases with with write buffer activity, this watchdog
+ * handler function will be evoked on the thread of execution of the
+ * worker thread.
+ */
+
+ rwb_wrflush(rwb);
+}
+
+/****************************************************************************
+ * Name: rwb_wrstarttimeout
+ ****************************************************************************/
+
+static void rwb_wrstarttimeout(FAR struct rwbuffer_s *rwb)
+{
+ /* CONFIG_FS_WRDELAY provides the delay period in milliseconds. CLK_TCK
+ * provides the clock tick of the system (frequency in Hz).
+ */
+
+ int ticks = (CONFIG_FS_WRDELAY + CLK_TCK/2) / CLK_TCK;
+ (void)work_queue(LPWORK, &rwb->work, rwb_wrtimeout, (FAR void *)rwb, ticks);
+}
+
+/****************************************************************************
+ * Name: rwb_wrcanceltimeout
+ ****************************************************************************/
+
+static inline void rwb_wrcanceltimeout(struct rwbuffer_s *rwb)
+{
+ (void)work_cancel(LPWORK, &rwb->work);
+}
+
+/****************************************************************************
+ * Name: rwb_writebuffer
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITEBUFFER
+static ssize_t rwb_writebuffer(FAR struct rwbuffer_s *rwb,
+ off_t startblock, uint32_t nblocks,
+ FAR const uint8_t *wrbuffer)
+{
+ int ret;
+
+ /* Write writebuffer Logic */
+
+ rwb_wrcanceltimeout(rwb);
+
+ /* First: Should we flush out our cache? We would do that if (1) we already
+ * buffering blocks and the next block writing is not in the same sequence,
+ * or (2) the number of blocks would exceed our allocated buffer capacity
+ */
+
+ if (((startblock != rwb->wrexpectedblock) && (rwb->wrnblocks)) ||
+ ((rwb->wrnblocks + nblocks) > rwb->wrmaxblocks))
+ {
+ fvdbg("writebuffer miss, expected: %08x, given: %08x\n",
+ rwb->wrexpectedblock, startblock);
+
+ /* Flush the write buffer */
+
+ ret = rwb->wrflush(rwb, rwb->wrbuffer, rwb->wrblockstart, rwb->wrnblocks);
+ if (ret < 0)
+ {
+ fdbg("ERROR: Error writing multiple from cache: %d\n", -ret);
+ return ret;
+ }
+
+ rwb_resetwrbuffer(rwb);
+ }
+
+ /* writebuffer is empty? Then initialize it */
+
+ if (!rwb->wrnblocks)
+ {
+ fvdbg("Fresh cache starting at block: 0x%08x\n", startblock);
+ rwb->wrblockstart = startblock;
+ }
+
+ /* Add data to cache */
+
+ fvdbg("writebuffer: copying %d bytes from %p to %p\n",
+ nblocks * wrb->blocksize, wrbuffer,
+ &rwb->wrbuffer[rwb->wrnblocks * rwb->blocksize]);
+ memcpy(&rwb->wrbuffer[rwb->wrnblocks * rwb->blocksize],
+ wrbuffer, nblocks * rwb->blocksize);
+
+ rwb->wrnblocks += nblocks;
+ rwb->wrexpectedblock = rwb->wrblockstart + rwb->wrnblocks;
+ rwb_wrstarttimeout(rwb);
+ return nblocks;
+}
+#endif
+
+/****************************************************************************
+ * Name: rwb_resetrhbuffer
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_READAHEAD
+static inline void rwb_resetrhbuffer(struct rwbuffer_s *rwb)
+{
+ /* We assume that the caller holds the readAheadBufferSemphore */
+
+ rwb->rhnblocks = 0;
+ rwb->rhblockstart = (off_t)-1;
+}
+#endif
+
+/****************************************************************************
+ * Name: rwb_bufferread
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_READAHEAD
+static inline void
+rwb_bufferread(struct rwbuffer_s *rwb, off_t startblock,
+ size_t nblocks, uint8_t **rdbuffer)
+{
+ /* We assume that (1) the caller holds the readAheadBufferSemphore, and (2)
+ * that the caller already knows that all of the blocks are in the
+ * read-ahead buffer.
+ */
+
+ /* Convert the units from blocks to bytes */
+
+ off_t blockoffset = startblock - rwb->rhblockstart;
+ off_t byteoffset = rwb->blocksize * blockoffset;
+ size_t nbytes = rwb->blocksize * nblocks;
+
+ /* Get the byte address in the read-ahead buffer */
+
+ uint8_t *rhbuffer = rwb->rhbuffer + byteoffset;
+
+ /* Copy the data from the read-ahead buffer into the IO buffer */
+
+ memcpy(*rdbuffer, rhbuffer, nbytes);
+
+ /* Update the caller's copy for the next address */
+
+ *rdbuffer += nbytes;
+}
+#endif
+
+/****************************************************************************
+ * Name: rwb_rhreload
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_READAHEAD
+static int rwb_rhreload(struct rwbuffer_s *rwb, off_t startblock)
+{
+ /* Get the block number +1 of the last block that will fit in the
+ * read-ahead buffer
+ */
+
+ off_t endblock = startblock + rwb->rhmaxblocks;
+ size_t nblocks;
+ int ret;
+
+ /* Reset the read buffer */
+
+ rwb_resetrhbuffer(rwb);
+
+ /* Make sure that we don't read past the end of the device */
+
+ if (endblock > rwb->nblocks)
+ {
+ endblock = rwb->nblocks;
+ }
+
+ nblocks = endblock - startblock;
+
+ /* Now perform the read */
+
+ ret = rwb->rhreload(rwb->dev, rwb->rhbuffer, startblock, nblocks);
+ if (ret == nblocks)
+ {
+ /* Update information about what is in the read-ahead buffer */
+
+ rwb->rhnblocks = nblocks;
+ rwb->rhblockstart = startblock;
+
+ /* The return value is not the number of blocks we asked to be loaded. */
+
+ return nblocks;
+ }
+
+ return -EIO;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Name: rwb_initialize
+ ****************************************************************************/
+
+int rwb_initialize(FAR struct rwbuffer_s *rwb)
+{
+ uint32_t allocsize;
+
+ /* Sanity checking */
+
+ DEBUGASSERT(rwb != NULL);
+ DEBUGASSERT(rwb->blocksize > 0);
+ DEBUGASSERT(rwb->nblocks > 0);
+ DEBUGASSERT(rwb->dev != NULL);
+
+ /* Setup so that rwb_uninitialize can handle a failure */
+
+#ifdef CONFIG_FS_WRITEBUFFER
+ DEBUGASSERT(rwb->wrflush!= NULL);
+ rwb->wrbuffer = NULL;
+#endif
+#ifdef CONFIG_FS_READAHEAD
+ DEBUGASSERT(rwb->rhreload != NULL);
+ rwb->rhbuffer = NULL;
+#endif
+
+#ifdef CONFIG_FS_WRITEBUFFER
+ fvdbg("Initialize the write buffer\n");
+
+ /* Initialize the write buffer access semaphore */
+
+ sem_init(&rwb->wrsem, 0, 1);
+
+ /* Initialize write buffer parameters */
+
+ rwb_resetwrbuffer(rwb);
+
+ /* Allocate the write buffer */
+
+ rwb->wrbuffer = NULL;
+ if (rwb->wrmaxblocks > 0)
+ {
+ allocsize = rwb->wrmaxblocks * rwb->blocksize;
+ rwb->wrbuffer = kmalloc(allocsize);
+ if (!rwb->wrbuffer)
+ {
+ fdbg("Write buffer kmalloc(%d) failed\n", allocsizee);
+ return -ENOMEM;
+ }
+ }
+
+ fvdbg("Write buffer size: %d bytes\n", allocsize);
+#endif /* CONFIG_FS_WRITEBUFFER */
+
+#ifdef CONFIG_FS_READAHEAD
+ fvdbg("Initialize the read-ahead buffer\n");
+
+ /* Initialize the read-ahead buffer access semaphore */
+
+ sem_init(&rwb->rhsem, 0, 1);
+
+ /* Initialize read-ahead buffer parameters */
+
+ rwb_resetrhbuffer(rwb);
+
+ /* Allocate the read-ahead buffer */
+
+ rwb->rhbuffer = NULL;
+ if (rwb->rhmaxblocks > 0)
+ {
+ allocsize = rwb->rhmaxblocks * rwb->blocksize;
+ rwb->rhbuffer = kmalloc(allocsize);
+ if (!rwb->rhbuffer)
+ {
+ fdbg("Read-ahead buffer kmalloc(%d) failed\n", allocsize);
+ return -ENOMEM;
+ }
+ }
+
+ fvdbg("Read-ahead buffer size: %d bytes\n", allocsize);
+#endif /* CONFIG_FS_READAHEAD */
+ return 0;
+}
+
+/****************************************************************************
+ * Name: rwb_uninitialize
+ ****************************************************************************/
+
+void rwb_uninitialize(FAR struct rwbuffer_s *rwb)
+{
+#ifdef CONFIG_FS_WRITEBUFFER
+ rwb_wrcanceltimeout(rwb);
+ sem_destroy(&rwb->wrsem);
+ if (rwb->wrbuffer)
+ {
+ kfree(rwb->wrbuffer);
+ }
+#endif
+
+#ifdef CONFIG_FS_READAHEAD
+ sem_destroy(&rwb->rhsem);
+ if (rwb->rhbuffer)
+ {
+ kfree(rwb->rhbuffer);
+ }
+#endif
+}
+
+/****************************************************************************
+ * Name: rwb_read
+ ****************************************************************************/
+
+int rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock, uint32_t nblocks,
+ FAR uint8_t *rdbuffer)
+{
+ uint32_t remaining;
+
+ fvdbg("startblock=%ld nblocks=%ld rdbuffer=%p\n",
+ (long)startblock, (long)nblocks, rdbuffer);
+
+#ifdef CONFIG_FS_WRITEBUFFER
+ /* If the new read data overlaps any part of the write buffer, then
+ * flush the write data onto the physical media before reading. We
+ * could attempt some more exotic handling -- but this simple logic
+ * is well-suited for simple streaming applications.
+ */
+
+ if (rwb->wrmaxblocks > 0)
+ {
+ /* If the write buffer overlaps the block(s) requested, then flush the
+ * write buffer.
+ */
+
+ rwb_semtake(&rwb->wrsem);
+ if (rwb_overlap(rwb->wrblockstart, rwb->wrnblocks, startblock, nblocks))
+ {
+ rwb_wrflush(rwb);
+ }
+ rwb_semgive(&rwb->wrsem);
+ }
+#endif
+
+#ifdef CONFIG_FS_READAHEAD
+ /* Loop until we have read all of the requested blocks */
+
+ rwb_semtake(&rwb->rhsem);
+ for (remaining = nblocks; remaining > 0;)
+ {
+ /* Is there anything in the read-ahead buffer? */
+
+ if (rwb->rhnblocks > 0)
+ {
+ off_t startblock = startblock;
+ size_t nbufblocks = 0;
+ off_t bufferend;
+
+ /* Loop for each block we find in the read-head buffer. Count the
+ * number of buffers that we can read from read-ahead buffer.
+ */
+
+ bufferend = rwb->rhblockstart + rwb->rhnblocks;
+
+ while ((startblock >= rwb->rhblockstart) &&
+ (startblock < bufferend) &&
+ (remaining > 0))
+ {
+ /* This is one more that we will read from the read ahead buffer */
+
+ nbufblocks++;
+
+ /* And one less that we will read from the media */
+
+ startblock++;
+ remaining--;
+ }
+
+ /* Then read the data from the read-ahead buffer */
+
+ rwb_bufferread(rwb, startblock, nbufblocks, &rdbuffer);
+ }
+
+ /* If we did not get all of the data from the buffer, then we have to refill
+ * the buffer and try again.
+ */
+
+ if (remaining > 0)
+ {
+ int ret = rwb_rhreload(rwb, startblock);
+ if (ret < 0)
+ {
+ fdbg("ERROR: Failed to fill the read-ahead buffer: %d\n", -ret);
+ return ret;
+ }
+ }
+ }
+
+ /* On success, return the number of blocks that we were requested to read.
+ * This is for compatibility with the normal return of a block driver read
+ * method
+ */
+
+ rwb_semgive(&rwb->rhsem);
+ return 0;
+#else
+ return rwb->rhreload(rwb->dev, startblock, nblocks, rdbuffer);
+#endif
+}
+
+/****************************************************************************
+ * Name: rwb_write
+ ****************************************************************************/
+
+int rwb_write(FAR struct rwbuffer_s *rwb, off_t startblock,
+ size_t nblocks, FAR const uint8_t *wrbuffer)
+{
+ int ret;
+
+#ifdef CONFIG_FS_READAHEAD
+ /* If the new write data overlaps any part of the read buffer, then
+ * flush the data from the read buffer. We could attempt some more
+ * exotic handling -- but this simple logic is well-suited for simple
+ * streaming applications.
+ */
+
+ rwb_semtake(&rwb->rhsem);
+ if (rwb_overlap(rwb->rhblockstart, rwb->rhnblocks, startblock, nblocks))
+ {
+ rwb_resetrhbuffer(rwb);
+ }
+ rwb_give(&rwb->rhsem);
+#endif
+
+#ifdef CONFIG_FS_WRITEBUFFER
+ fvdbg("startblock=%d wrbuffer=%p\n", startblock, wrbuffer);
+
+ /* Use the block cache unless the buffer size is bigger than block cache */
+
+ if (nblocks > rwb->wrmaxblocks)
+ {
+ /* First flush the cache */
+
+ rwb_semtake(&rwb->wrsem);
+ rwb_wrflush(rwb);
+ rwb_semgive(&rwb->wrsem);
+
+ /* Then transfer the data directly to the media */
+
+ ret = rwb->wrflush(rwb->dev, startblock, nblocks, wrbuffer);
+ }
+ else
+ {
+ /* Buffer the data in the write buffer */
+
+ ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer);
+ }
+
+ /* On success, return the number of blocks that we were requested to write.
+ * This is for compatibility with the normal return of a block driver write
+ * method
+ */
+
+ return ret;
+
+#else
+
+ return rwb->wrflush(rwb->dev, startblock, nblocks, wrbuffer);
+
+#endif
+}
+
+/****************************************************************************
+ * Name: rwb_mediaremoved
+ *
+ * Description:
+ * The following function is called when media is removed
+ *
+ ****************************************************************************/
+
+int rwb_mediaremoved(FAR struct rwbuffer_s *rwb)
+{
+#ifdef CONFIG_FS_WRITEBUFFER
+ rwb_semtake(&rwb->wrsem);
+ rwb_resetwrbuffer(rwb);
+ rwb_semgive(&rwb->wrsem);
+#endif
+
+#ifdef CONFIG_FS_READAHEAD
+ rwb_semtake(&rwb->rhsem);
+ rwb_resetrhbuffer(rwb);
+ rwb_semgive(&rwb->rhsem);
+#endif
+ return 0;
+}
+
+#endif /* CONFIG_FS_WRITEBUFFER || CONFIG_FS_READAHEAD */
+
diff --git a/nuttx/drivers/sensors/Kconfig b/nuttx/drivers/sensors/Kconfig
new file mode 100644
index 000000000..386cdc3a8
--- /dev/null
+++ b/nuttx/drivers/sensors/Kconfig
@@ -0,0 +1,33 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+config LIS331DL
+ bool "ST LIS331DL device support"
+ default n
+ select I2C
+
+config I2C_LM75
+ bool
+ default y if LM75
+
+config LM75
+ bool "STMicro LM-75 Temperature Sensor support"
+ default n
+ select I2C
+ select I2C_LM75
+
+config DEBUG_LM75
+ bool "Enable LM-75 debug"
+ default n
+ depends on LM75
+
+config QENCODER
+ bool "Qencoder"
+ default n
+
+config DEBUG_QENCODER
+ bool "Enable Qencoder Debug"
+ default n
+ depends on QENCODER
+
diff --git a/nuttx/drivers/sensors/Make.defs b/nuttx/drivers/sensors/Make.defs
new file mode 100644
index 000000000..866ccb053
--- /dev/null
+++ b/nuttx/drivers/sensors/Make.defs
@@ -0,0 +1,60 @@
+############################################################################
+# drivers/sensors/Make.defs
+#
+# Copyright (C) 2011 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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 sensor drivers
+# These drivers depend on I2C support
+
+ifeq ($(CONFIG_I2C),y)
+
+ifeq ($(CONFIG_I2C_TRANSFER),y)
+ CSRCS += lis331dl.c
+endif
+
+ifeq ($(CONFIG_I2C_LM75),y)
+ CSRCS += lm75.c
+endif
+endif
+
+# Quadrature encoder upper half
+
+ifeq ($(CONFIG_QENCODER),y)
+ CSRCS += qencoder.c
+endif
+
+# Include sensor driver build support
+
+DEPPATH += --dep-path sensors
+VPATH += :sensors
+CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/sensors}
diff --git a/nuttx/drivers/sensors/lis331dl.c b/nuttx/drivers/sensors/lis331dl.c
new file mode 100644
index 000000000..2117a7ebd
--- /dev/null
+++ b/nuttx/drivers/sensors/lis331dl.c
@@ -0,0 +1,320 @@
+/****************************************************************************
+ * drivers/sensors/lis331dl.c
+ *
+ * Copyright (C) 2011 Uros Platise. All rights reserved.
+ *
+ * Authors: Uros Platise <uros.platise@isotel.eu>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/** \file
+ * \author Uros Platise
+ * \brief ST LIS331DL I2C Device Driver
+ **/
+
+#include <nuttx/config.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/sensors/lis331dl.h>
+
+/************************************************************************************
+ * LIS331DL Internal Registers
+ ************************************************************************************/
+
+#define ST_LIS331DL_WHOAMI 0x0F /* who am I register */
+#define ST_LIS331DL_WHOAMI_VALUE 0x3B /* Valid result is 0x3B */
+
+#define ST_LIS331DL_CTRL_REG1 0x20
+#define ST_LIS331DL_CR1_DR 0x80 /* Data-rate selection 0: 100 Hz, 1: 400 Hz */
+#define ST_LIS331DL_CR1_PD 0x40 /* Active Mode (1) / Power-down (0) */
+#define ST_LIS331DL_CR1_FS 0x20 /* Full Scale (1) +-9g or Normal Scale (0) +-2g */
+#define ST_LIS331DL_CR1_ST 0x18 /* Self test enable */
+#define ST_LIS331DL_CR1_ZEN 0x04 /* Z-Axis Enable */
+#define ST_LIS331DL_CR1_YEN 0x02 /* Y-Axis Enable */
+#define ST_LIS331DL_CR1_XEN 0x01 /* X-Axis Enable */
+
+#define ST_LIS331DL_CTRL_REG2 0x21
+#define ST_LIS331DL_CTRL_REG3 0x22
+
+#define ST_LIS331DL_HP_FILTER_RESET 0x23
+
+#define ST_LIS331DL_STATUS_REG 0x27 /* Status Register */
+#define ST_LIS331DL_SR_ZYXOR 0x80 /* OR'ed X,Y and Z data over-run */
+#define ST_LIS331DL_SR_ZOR 0x40 /* individual data over-run ... */
+#define ST_LIS331DL_SR_YOR 0x20
+#define ST_LIS331DL_SR_XOR 0x10
+#define ST_LIS331DL_SR_ZYXDA 0x08 /* OR'ed X,Y and Z data available */
+#define ST_LIS331DL_SR_ZDA 0x04 /* individual data available ... */
+#define ST_LIS331DL_SR_YDA 0x02
+#define ST_LIS331DL_SR_XDA 0x01
+
+#define ST_LIS331DL_OUT_X 0x29
+#define ST_LIS331DL_OUT_Y 0x2B
+#define ST_LIS331DL_OUT_Z 0x2D
+
+
+/************************************************************************************
+ * Private Data Types
+ ************************************************************************************/
+
+struct lis331dl_dev_s {
+ struct i2c_dev_s * i2c;
+
+ uint8_t address;
+ struct lis331dl_vector_s a;
+ uint8_t cr1;
+ uint8_t cr2;
+ uint8_t cr3;
+};
+
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/** LIS331DL Access with range check
+ *
+ * \param dev LIS331 DL Private Structure
+ * \param subaddr LIS331 Sub Address
+ * \param buf Pointer to buffer, either for read or write access
+ * \param length when >0 it denotes read access, when <0 it denotes write access of -length
+ * \return OK on success or errno is set.
+ **/
+int lis331dl_access(struct lis331dl_dev_s * dev, uint8_t subaddr, uint8_t *buf, int length)
+{
+ uint16_t flags = 0;
+ int retval;
+
+ if (length > 0) {
+ flags = I2C_M_READ;
+ }
+ else {
+ flags = I2C_M_NORESTART;
+ length = -length;
+ }
+
+ /* Check valid address ranges and set auto address increment flag */
+
+ if (subaddr == 0x0F) {
+ if (length > 1) length = 1;
+ }
+ else if (subaddr >= 0x20 && subaddr < 0x24) {
+ if (length > (0x24 - subaddr) ) length = 0x24 - subaddr;
+ }
+ else if (subaddr >= 0x27 && subaddr < 0x2E) {
+ if (length > (0x2E - subaddr) ) length = 0x2E - subaddr;
+ }
+ else if (subaddr >= 0x30 && subaddr < 0x40) {
+ if (length > (0x40 - subaddr) ) length = 0x40 - subaddr;
+ }
+ else {
+ errno = EFAULT;
+ return ERROR;
+ }
+
+ if (length > 1) subaddr |= 0x80;
+
+ /* Create message and send */
+
+ struct i2c_msg_s msgv[2] = {
+ {
+ .addr = dev->address,
+ .flags = 0,
+ .buffer = &subaddr,
+ .length = 1
+ },
+ {
+ .addr = dev->address,
+ .flags = flags,
+ .buffer = buf,
+ .length = length
+ }
+ };
+
+ if ( (retval = I2C_TRANSFER(dev->i2c, msgv, 2)) == OK )
+ return length;
+
+ return retval;
+}
+
+
+int lis331dl_readregs(struct lis331dl_dev_s * dev)
+{
+ if (lis331dl_access(dev, ST_LIS331DL_CTRL_REG1, &dev->cr1, 3) != 3) return ERROR;
+ return OK;
+}
+
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+struct lis331dl_dev_s * lis331dl_init(struct i2c_dev_s * i2c, uint16_t address)
+{
+ struct lis331dl_dev_s * dev;
+ uint8_t retval;
+
+ ASSERT(i2c);
+ ASSERT(address);
+
+ if ( (dev = kmalloc( sizeof(struct lis331dl_dev_s) )) == NULL ) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(struct lis331dl_dev_s));
+ dev->i2c = i2c;
+ dev->address = address;
+
+ /* Probe device */
+
+ if (lis331dl_access(dev, ST_LIS331DL_WHOAMI, &retval, 1) > 0) {
+
+ /* Check chip identification, in the future several more compatible parts
+ * may be added here.
+ */
+
+ if (retval == ST_LIS331DL_WHOAMI_VALUE) {
+
+ /* Copy LIS331DL registers to our private structure and power-up device */
+
+ if (lis331dl_readregs(dev)==OK && lis331dl_powerup(dev)==OK) {
+
+ /* Normal exit point */
+ errno = 0;
+ return dev;
+ }
+ retval = errno;
+ }
+
+ /* Otherwise, we mark an invalid device found at given address */
+ retval = ENODEV;
+ }
+ else {
+ /* No response at given address is marked as */
+ retval = EFAULT;
+ }
+
+ /* Error exit */
+ kfree(dev);
+ errno = retval;
+ return NULL;
+}
+
+
+int lis331dl_deinit(struct lis331dl_dev_s * dev)
+{
+ ASSERT(dev);
+
+ lis331dl_powerdown(dev);
+ kfree(dev);
+
+ return OK;
+}
+
+
+int lis331dl_powerup(struct lis331dl_dev_s * dev)
+{
+ dev->cr1 = ST_LIS331DL_CR1_PD |
+ ST_LIS331DL_CR1_ZEN | ST_LIS331DL_CR1_YEN | ST_LIS331DL_CR1_XEN;
+ dev->cr2 = 0;
+ dev->cr3 = 0;
+
+ if (lis331dl_access(dev, ST_LIS331DL_CTRL_REG1, &dev->cr1, -3) == 3) return OK;
+ return ERROR;
+}
+
+
+int lis331dl_powerdown(struct lis331dl_dev_s * dev)
+{
+ dev->cr1 = ST_LIS331DL_CR1_ZEN | ST_LIS331DL_CR1_YEN | ST_LIS331DL_CR1_XEN;
+ dev->cr2 = 0;
+ dev->cr3 = 0;
+
+ if (lis331dl_access(dev, ST_LIS331DL_CTRL_REG1, &dev->cr1, -3) == 3) return OK;
+ return ERROR;
+}
+
+
+int lis331dl_setconversion(struct lis331dl_dev_s * dev, bool full, bool fast)
+{
+ dev->cr1 = ST_LIS331DL_CR1_PD |
+ (full ? ST_LIS331DL_CR1_FS : 0) | (fast ? ST_LIS331DL_CR1_DR : 0) |
+ ST_LIS331DL_CR1_ZEN | ST_LIS331DL_CR1_YEN | ST_LIS331DL_CR1_XEN;
+
+ if (lis331dl_access(dev, ST_LIS331DL_CTRL_REG1, &dev->cr1, -1) == 1) return OK;
+ return ERROR;
+}
+
+
+int lis331dl_getprecision(struct lis331dl_dev_s * dev)
+{
+ if (dev->cr1 & ST_LIS331DL_CR1_FS)
+ return 9200/127; /* typ. 9.2g full scale */
+ return 2300/127; /* typ. 2.3g full scale */
+}
+
+
+int lis331dl_getsamplerate(struct lis331dl_dev_s * dev)
+{
+ if (dev->cr1 & ST_LIS331DL_CR1_DR)
+ return 400;
+ return 100;
+}
+
+
+const struct lis331dl_vector_s * lis331dl_getreadings(struct lis331dl_dev_s * dev)
+{
+ uint8_t retval[7];
+
+ ASSERT(dev);
+
+ if (lis331dl_access(dev, ST_LIS331DL_STATUS_REG, retval, 7) == 7) {
+
+ /* If result is not yet ready, return NULL */
+
+ if ( !(retval[0] & ST_LIS331DL_SR_ZYXDA) ) {
+ errno = EAGAIN;
+ return NULL;
+ }
+
+ dev->a.x = retval[2];
+ dev->a.y = retval[4];
+ dev->a.z = retval[6];
+ return &dev->a;
+ }
+
+ return NULL;
+}
diff --git a/nuttx/drivers/sensors/lm75.c b/nuttx/drivers/sensors/lm75.c
new file mode 100644
index 000000000..2d3346447
--- /dev/null
+++ b/nuttx/drivers/sensors/lm75.c
@@ -0,0 +1,537 @@
+/****************************************************************************
+ * drivers/sensors/lm75.c
+ * Character driver for the STMicro LM-75 Temperature Sensor
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdlib.h>
+#include <fixedmath.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/i2c.h>
+#include <nuttx/sensors/lm75.h>
+
+#if defined(CONFIG_I2C) && defined(CONFIG_I2C_LM75)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Centigrade to Fahrenheit conversion: F = 9*C/5 + 32 */
+
+#define B16_9DIV5 (9 * 65536 / 5)
+#define B16_32 (32 * 65536)
+
+/* Debug for this file only */
+
+#ifdef CONFIG_DEBUG_LM75
+# define lm75dbg dbg
+#else
+# ifdef CONFIG_CPP_HAVE_VARARGS
+# define lm75dbg(x...)
+# else
+# define lm75dbg (void)
+# endif
+#endif
+
+/****************************************************************************
+ * Private
+ ****************************************************************************/
+
+struct lm75_dev_s
+{
+ FAR struct i2c_dev_s *i2c; /* I2C interface */
+ uint8_t addr; /* I2C address */
+ bool fahrenheit; /* true: temperature will be reported in fahrenheit */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* I2C Helpers */
+
+static int lm75_readb16(FAR struct lm75_dev_s *priv, uint8_t regaddr,
+ FAR b16_t *regvalue);
+static int lm75_writeb16(FAR struct lm75_dev_s *priv, uint8_t regaddr,
+ b16_t regval);
+static int lm75_readtemp(FAR struct lm75_dev_s *priv, FAR b16_t *temp);
+static int lm75_readconf(FAR struct lm75_dev_s *priv, FAR uint8_t *conf);
+static int lm75_writeconf(FAR struct lm75_dev_s *priv, uint8_t conf);
+
+/* Character driver methods */
+
+static int lm75_open(FAR struct file *filep);
+static int lm75_close(FAR struct file *filep);
+static ssize_t lm75_read(FAR struct file *, FAR char *, size_t);
+static ssize_t lm75_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+static int lm75_ioctl(FAR struct file *filep,int cmd,unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_lm75fops =
+{
+ lm75_open,
+ lm75_close,
+ lm75_read,
+ lm75_write,
+ 0,
+ lm75_ioctl
+#ifndef CONFIG_DISABLE_POLL
+ , 0
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Name: lm75_readb16
+ *
+ * Description:
+ * Read a 16-bit register (LM75_TEMP_REG, LM75_THYS_REG, or LM75_TOS_REG)
+ *
+ ****************************************************************************/
+
+static int lm75_readb16(FAR struct lm75_dev_s *priv, uint8_t regaddr,
+ FAR b16_t *regvalue)
+{
+ uint8_t buffer[2];
+ int ret;
+
+ /* Write the register address */
+
+ I2C_SETADDRESS(priv->i2c, priv->addr, 7);
+ ret = I2C_WRITE(priv->i2c, &regaddr, 1);
+ if (ret < 0)
+ {
+ lm75dbg("I2C_WRITE failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Restart and read 16-bits from the register (discarding 7) */
+
+ ret = I2C_READ(priv->i2c, buffer, 2);
+ if (ret < 0)
+ {
+ lm75dbg("I2C_READ failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Data format is: TTTTTTTT Txxxxxxx where TTTTTTTTT is a nine-bit,
+ * signed temperature value with LSB = 0.5 degrees centigrade. So the
+ * raw data is b8_t
+ */
+
+ *regvalue = b8tob16((b8_t)buffer[0] << 8 | (b8_t)buffer[1]);
+ lm75dbg("addr: %02x value: %08x ret: %d\n", regaddr, *regvalue, ret);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: lm75_writeb16
+ *
+ * Description:
+ * Write to a 16-bit register (LM75_TEMP_REG, LM75_THYS_REG, or LM75_TOS_REG)
+ *
+ ****************************************************************************/
+
+static int lm75_writeb16(FAR struct lm75_dev_s *priv, uint8_t regaddr,
+ b16_t regval)
+{
+ uint8_t buffer[3];
+ b8_t regb8;
+
+ lm75dbg("addr: %02x value: %08x\n", regaddr, regval);
+
+ /* Set up a 3 byte message to send */
+
+ buffer[0] = regaddr;
+
+ regb8 = b16tob8(regval);
+ buffer[1] = (uint8_t)(regb8 >> 8);
+ buffer[2] = (uint8_t)regb8;
+
+ /* Write the register address followed by the data (no RESTART) */
+
+ I2C_SETADDRESS(priv->i2c, priv->addr, 7);
+ return I2C_WRITE(priv->i2c, buffer, 3);
+}
+
+/****************************************************************************
+ * Name: lm75_readtemp
+ *
+ * Description:
+ * Read the temperature register with special scaling (LM75_TEMP_REG)
+ *
+ ****************************************************************************/
+
+static int lm75_readtemp(FAR struct lm75_dev_s *priv, FAR b16_t *temp)
+{
+ b16_t temp16;
+ int ret;
+
+ /* Read the raw temperature data (b16_t) */
+
+ ret = lm75_readb16(priv, LM75_TEMP_REG, &temp16);
+ if (ret < 0)
+ {
+ lm75dbg("lm75_readb16 failed: %d\n", ret);
+ return ret;
+ }
+ lm75dbg("Centigrade: %08x\n", temp16);
+
+ /* Was fahrenheit requested? */
+
+ if (priv->fahrenheit)
+ {
+ /* Centigrade to Fahrenheit conversion: F = 9*C/5 + 32 */
+
+ temp16 = b16mulb16(temp16, B16_9DIV5) + B16_32;
+ lm75dbg("Fahrenheit: %08x\n", temp16);
+ }
+
+ *temp = temp16;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: lm75_readconf
+ *
+ * Description:
+ * Read the 8-bit LM75 configuration register
+ *
+ ****************************************************************************/
+
+static int lm75_readconf(FAR struct lm75_dev_s *priv, FAR uint8_t *conf)
+{
+ uint8_t buffer;
+ int ret;
+
+ /* Write the configuration register address */
+
+ I2C_SETADDRESS(priv->i2c, priv->addr, 7);
+
+ buffer = LM75_CONF_REG;
+ ret = I2C_WRITE(priv->i2c, &buffer, 1);
+ if (ret < 0)
+ {
+ lm75dbg("I2C_WRITE failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Restart and read 8-bits from the register */
+
+ ret = I2C_READ(priv->i2c, conf, 1);
+ lm75dbg("conf: %02x ret: %d\n", *conf, ret);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: lm75_writeconf
+ *
+ * Description:
+ * Write to a 8-bit LM75 configuration register.
+ *
+ ****************************************************************************/
+
+static int lm75_writeconf(FAR struct lm75_dev_s *priv, uint8_t conf)
+{
+ uint8_t buffer[2];
+
+ lm75dbg("conf: %02x\n", conf);
+
+ /* Set up a 2 byte message to send */
+
+ buffer[0] = LM75_CONF_REG;
+ buffer[1] = conf;
+
+ /* Write the register address followed by the data (no RESTART) */
+
+ I2C_SETADDRESS(priv->i2c, priv->addr, 7);
+ return I2C_WRITE(priv->i2c, buffer, 2);
+}
+
+/****************************************************************************
+ * Name: lm75_open
+ *
+ * Description:
+ * This function is called whenever the LM-75 device is opened.
+ *
+ ****************************************************************************/
+
+static int lm75_open(FAR struct file *filep)
+{
+ return OK;
+}
+
+/****************************************************************************
+ * Name: lm75_close
+ *
+ * Description:
+ * This routine is called when the LM-75 device is closed.
+ *
+ ****************************************************************************/
+
+static int lm75_close(FAR struct file *filep)
+{
+ return OK;
+}
+
+/****************************************************************************
+ * Name: lm75_read
+ ****************************************************************************/
+
+static ssize_t lm75_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct lm75_dev_s *priv = inode->i_private;
+ FAR b16_t *ptr;
+ ssize_t nsamples;
+ int i;
+ int ret;
+
+ /* How many samples were requested to get? */
+
+ nsamples = buflen / sizeof(b16_t);
+ ptr = (FAR b16_t *)buffer;
+
+ lm75dbg("buflen: %d nsamples: %d\n", buflen, nsamples);
+
+ /* Get the requested number of samples */
+
+ for (i = 0; i < nsamples; i++)
+ {
+ b16_t temp;
+
+ /* Read the next b16_t temperature value */
+
+ ret = lm75_readtemp(priv, &temp);
+ if (ret < 0)
+ {
+ lm75dbg("lm75_readtemp failed: %d\n",ret);
+ return (ssize_t)ret;
+ }
+
+ /* Save the temperature value in the user buffer */
+
+ *ptr++ = temp;
+ }
+
+ return nsamples * sizeof(b16_t);
+}
+
+/****************************************************************************
+ * Name: lm75_write
+ ****************************************************************************/
+
+static ssize_t lm75_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen)
+{
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: lm75_ioctl
+ ****************************************************************************/
+
+static int lm75_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct lm75_dev_s *priv = inode->i_private;
+ int ret = OK;
+
+ switch (cmd)
+ {
+ /* Read from the configuration register. Arg: uint8_t* pointer */
+
+ case SNIOC_READCONF:
+ {
+ FAR uint8_t *ptr = (FAR uint8_t *)((uintptr_t)arg);
+ ret = lm75_readconf(priv, ptr);
+ lm75dbg("conf: %02x ret: %d\n", *ptr, ret);
+ }
+ break;
+
+ /* Wrtie to the configuration register. Arg: uint8_t value */
+
+ case SNIOC_WRITECONF:
+ ret = lm75_writeconf(priv, (uint8_t)arg);
+ lm75dbg("conf: %02x ret: %d\n", *(uint8_t*)arg, ret);
+ break;
+
+ /* Shutdown the LM75, Arg: None */
+
+ case SNIOC_SHUTDOWN:
+ {
+ uint8_t conf;
+ ret = lm75_readconf(priv, &conf);
+ if (ret == OK)
+ {
+ ret = lm75_writeconf(priv, conf | LM75_CONF_SHUTDOWN);
+ }
+ lm75dbg("conf: %02x ret: %d\n", conf | LM75_CONF_SHUTDOWN, ret);
+ }
+ break;
+
+ /* Powerup the LM75, Arg: None */
+
+ case SNIOC_POWERUP:
+ {
+ uint8_t conf;
+ ret = lm75_readconf(priv, &conf);
+ if (ret == OK)
+ {
+ ret = lm75_writeconf(priv, conf & ~LM75_CONF_SHUTDOWN);
+ }
+ lm75dbg("conf: %02x ret: %d\n", conf & ~LM75_CONF_SHUTDOWN, ret);
+ }
+ break;
+
+ /* Report samples in Fahrenheit */
+
+ case SNIOC_FAHRENHEIT:
+ priv->fahrenheit = true;
+ lm75dbg("Fahrenheit\n");
+ break;
+
+ /* Report Samples in Centigrade */
+
+ case SNIOC_CENTIGRADE:
+ priv->fahrenheit = false;
+ lm75dbg("Centigrade\n");
+ break;
+
+ /* Read THYS temperature register. Arg: b16_t* pointer */
+
+ case SNIOC_READTHYS:
+ {
+ FAR b16_t *ptr = (FAR b16_t *)((uintptr_t)arg);
+ ret = lm75_readb16(priv, LM75_THYS_REG, ptr);
+ lm75dbg("THYS: %08x ret: %d\n", *ptr, ret);
+ }
+ break;
+
+ /* Write THYS temperature register. Arg: b16_t value */
+
+ case SNIOC_WRITETHYS:
+ ret = lm75_writeb16(priv, LM75_THYS_REG, (b16_t)arg);
+ lm75dbg("THYS: %08x ret: %d\n", (b16_t)arg, ret);
+ break;
+
+ /* Read TOS (Over-temp Shutdown Threshold) Register. Arg: b16_t* pointer */
+
+ case SNIOC_READTOS:
+ {
+ FAR b16_t *ptr = (FAR b16_t *)((uintptr_t)arg);
+ ret = lm75_readb16(priv, LM75_TOS_REG, ptr);
+ lm75dbg("TOS: %08x ret: %d\n", *ptr, ret);
+ }
+ break;
+
+ /* Write TOS (Over-temp Shutdown Threshold) Register. Arg: b16_t value */
+
+ case SNIOC_WRITRETOS:
+ ret = lm75_writeb16(priv, LM75_TOS_REG, (b16_t)arg);
+ lm75dbg("TOS: %08x ret: %d\n", (b16_t)arg, ret);
+ break;
+
+ default:
+ lm75dbg("Unrecognized cmd: %d\n", cmd);
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: lm75_register
+ *
+ * Description:
+ * Register the LM-75 character device as 'devpath'
+ *
+ * Input Parameters:
+ * devpath - The full path to the driver to register. E.g., "/dev/temp0"
+ * i2c - An instance of the I2C interface to use to communicate with LM75
+ * addr - The I2C address of the LM-75. The base I2C address of the LM75
+ * is 0x48. Bits 0-3 can be controlled to get 8 unique addresses from 0x48
+ * through 0x4f.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int lm75_register(FAR const char *devpath, FAR struct i2c_dev_s *i2c, uint8_t addr)
+{
+ FAR struct lm75_dev_s *priv;
+ int ret;
+
+ /* Initialize the LM-75 device structure */
+
+ priv = (FAR struct lm75_dev_s *)malloc(sizeof(struct lm75_dev_s));
+ if (!priv)
+ {
+ lm75dbg("Failed to allocate instance\n");
+ return -ENOMEM;
+ }
+
+ priv->i2c = i2c;
+ priv->addr = addr;
+ priv->fahrenheit = false;
+
+ /* Register the character driver */
+
+ ret = register_driver(devpath, &g_lm75fops, 0666, priv);
+ if (ret < 0)
+ {
+ lm75dbg("Failed to register driver: %d\n", ret);
+ free(priv);
+ }
+ return ret;
+}
+#endif /* CONFIG_I2C && CONFIG_I2C_LM75 */
diff --git a/nuttx/drivers/sensors/qencoder.c b/nuttx/drivers/sensors/qencoder.c
new file mode 100644
index 000000000..a56adec9a
--- /dev/null
+++ b/nuttx/drivers/sensors/qencoder.c
@@ -0,0 +1,402 @@
+/****************************************************************************
+ * drivers/sensors/qencoder.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Compilation Switches
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/sensors/qencoder.h>
+
+#include <arch/irq.h>
+
+#ifdef CONFIG_QENCODER
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Debug ********************************************************************/
+/* Non-standard debug that may be enabled just for testing PWM */
+
+#ifdef CONFIG_DEBUG_QENCODER
+# define qedbg dbg
+# define qevdbg vdbg
+# define qelldbg lldbg
+# define qellvdbg llvdbg
+#else
+# define qedbg(x...)
+# define qevdbg(x...)
+# define qelldbg(x...)
+# define qellvdbg(x...)
+#endif
+
+/****************************************************************************
+ * Private Type Definitions
+ ****************************************************************************/
+
+/* This structure describes the state of the upper half drivere */
+
+struct qe_upperhalf_s
+{
+ uint8_t crefs; /* The number of times the device has been opened */
+ sem_t exclsem; /* Supports mutual exclusion */
+ FAR struct qe_lowerhalf_s *lower; /* lower-half state */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int qe_open(FAR struct file *filep);
+static int qe_close(FAR struct file *filep);
+static ssize_t qe_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
+static ssize_t qe_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+static int qe_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_qeops =
+{
+ qe_open, /* open */
+ qe_close, /* close */
+ qe_read, /* read */
+ qe_write, /* write */
+ 0, /* seek */
+ qe_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , 0 /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/************************************************************************************
+ * Name: qe_open
+ *
+ * Description:
+ * This function is called whenever the PWM device is opened.
+ *
+ ************************************************************************************/
+
+static int qe_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct qe_upperhalf_s *upper = inode->i_private;
+ uint8_t tmp;
+ int ret;
+
+ qevdbg("crefs: %d\n", upper->crefs);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0)
+ {
+ ret = -errno;
+ goto errout;
+ }
+
+ /* Increment the count of references to the device. If this the first
+ * time that the driver has been opened for this device, then initialize
+ * the device.
+ */
+
+ tmp = upper->crefs + 1;
+ if (tmp == 0)
+ {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ goto errout_with_sem;
+ }
+
+ /* Check if this is the first time that the driver has been opened. */
+
+ if (tmp == 1)
+ {
+ FAR struct qe_lowerhalf_s *lower = upper->lower;
+
+ /* Yes.. perform one time hardware initialization. */
+
+ DEBUGASSERT(lower->ops->setup != NULL);
+ qevdbg("calling setup\n");
+
+ ret = lower->ops->setup(lower);
+ if (ret < 0)
+ {
+ goto errout_with_sem;
+ }
+ }
+
+ /* Save the new open count on success */
+
+ upper->crefs = tmp;
+ ret = OK;
+
+errout_with_sem:
+ sem_post(&upper->exclsem);
+
+errout:
+ return ret;
+}
+
+/************************************************************************************
+ * Name: qe_close
+ *
+ * Description:
+ * This function is called when the PWM device is closed.
+ *
+ ************************************************************************************/
+
+static int qe_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct qe_upperhalf_s *upper = inode->i_private;
+ int ret;
+
+ qevdbg("crefs: %d\n", upper->crefs);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0)
+ {
+ ret = -errno;
+ goto errout;
+ }
+
+ /* Decrement the references to the driver. If the reference count will
+ * decrement to 0, then uninitialize the driver.
+ */
+
+ if (upper->crefs > 1)
+ {
+ upper->crefs--;
+ }
+ else
+ {
+ FAR struct qe_lowerhalf_s *lower = upper->lower;
+
+ /* There are no more references to the port */
+
+ upper->crefs = 0;
+
+ /* Disable the PWM device */
+
+ DEBUGASSERT(lower->ops->shutdown != NULL);
+ qevdbg("calling shutdown: %d\n");
+
+ lower->ops->shutdown(lower);
+ }
+ ret = OK;
+
+//errout_with_sem:
+ sem_post(&upper->exclsem);
+
+errout:
+ return ret;
+}
+
+/************************************************************************************
+ * Name: qe_read
+ *
+ * Description:
+ * A dummy read method. This is provided only to satsify the VFS layer.
+ *
+ ************************************************************************************/
+
+static ssize_t qe_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ /* Return zero -- usually meaning end-of-file */
+
+ return 0;
+}
+
+/************************************************************************************
+ * Name: qe_write
+ *
+ * Description:
+ * A dummy write method. This is provided only to satsify the VFS layer.
+ *
+ ************************************************************************************/
+
+static ssize_t qe_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+{
+ /* Return a failure */
+
+ return -EPERM;
+}
+
+/************************************************************************************
+ * Name: qe_ioctl
+ *
+ * Description:
+ * The standard ioctl method. This is where ALL of the PWM work is done.
+ *
+ ************************************************************************************/
+
+static int qe_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct qe_upperhalf_s *upper = inode->i_private;
+ FAR struct qe_lowerhalf_s *lower = upper->lower;
+ int ret;
+
+ qevdbg("cmd: %d arg: %ld\n", cmd, arg);
+ DEBUGASSERT(upper && lower);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Handle built-in ioctl commands */
+
+ switch (cmd)
+ {
+ /* QEIOC_POSITION - Get the current position from the encoder.
+ * Argument: int32_t pointer to the location to return the position.
+ */
+
+ case QEIOC_POSITION:
+ {
+ FAR int32_t *ptr = (FAR int32_t *)((uintptr_t)arg);
+ DEBUGASSERT(lower->ops->position != NULL && ptr);
+ ret = lower->ops->position(lower, ptr);
+ }
+ break;
+
+ /* QEIOC_RESET - Reset the position to zero.
+ * Argument: None
+ */
+
+ case QEIOC_RESET:
+ {
+ DEBUGASSERT(lower->ops->reset != NULL);
+ ret = lower->ops->reset(lower);
+ }
+ break;
+
+ /* Any unrecognized IOCTL commands might be platform-specific ioctl commands */
+
+ default:
+ {
+ qevdbg("Forwarding unrecognized cmd: %d arg: %ld\n", cmd, arg);
+ DEBUGASSERT(lower->ops->ioctl != NULL);
+ ret = lower->ops->ioctl(lower, cmd, arg);
+ }
+ break;
+ }
+
+ sem_post(&upper->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: qe_register
+ *
+ * Description:
+ * Register the Quadrature Encoder lower half device as 'devpath'
+ *
+ * Input Parameters:
+ * devpath - The full path to the driver to register. E.g., "/dev/qe0"
+ * lower - An instance of the lower half interface
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure. The following
+ * possible error values may be returned (most are returned by
+ * register_driver()):
+ *
+ * EINVAL - 'path' is invalid for this operation
+ * EEXIST - An inode already exists at 'path'
+ * ENOMEM - Failed to allocate in-memory resources for the operation
+ *
+ ****************************************************************************/
+
+int qe_register(FAR const char *devpath, FAR struct qe_lowerhalf_s *lower)
+{
+ FAR struct qe_upperhalf_s *upper;
+
+ /* Allocate the upper-half data structure */
+
+ upper = (FAR struct qe_upperhalf_s *)zalloc(sizeof(struct qe_upperhalf_s));
+ if (!upper)
+ {
+ qedbg("Allocation failed\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the PWM device structure (it was already zeroed by zalloc()) */
+
+ sem_init(&upper->exclsem, 0, 1);
+ upper->lower = lower;
+
+ /* Register the PWM device */
+
+ qevdbg("Registering %s\n", devpath);
+ return register_driver(devpath, &g_qeops, 0666, upper);
+}
+
+#endif /* CONFIG_QENCODER */
diff --git a/nuttx/drivers/sercomm/Kconfig b/nuttx/drivers/sercomm/Kconfig
new file mode 100644
index 000000000..ae2bf3130
--- /dev/null
+++ b/nuttx/drivers/sercomm/Kconfig
@@ -0,0 +1,4 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
diff --git a/nuttx/drivers/sercomm/Make.defs b/nuttx/drivers/sercomm/Make.defs
new file mode 100644
index 000000000..0cf93d4c8
--- /dev/null
+++ b/nuttx/drivers/sercomm/Make.defs
@@ -0,0 +1,55 @@
+############################################################################
+# drivers/serial/Make.defs
+#
+# Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+# File descriptor support is needed for this driver
+
+ifneq ($(CONFIG_NFILE_DESCRIPTORS),0)
+
+# The sercomm driver should not be build for all platforms. Only build it
+# is so configured
+
+ifeq ($(CONFIG_SERCOMM_CONSOLE),y)
+
+# Include serial drivers
+
+CSRCS += console.c uart.c
+
+# Include sercomm build support
+
+DEPPATH += --dep-path sercomm
+VPATH += :sercomm
+
+endif
+endif
diff --git a/nuttx/drivers/sercomm/README.txt b/nuttx/drivers/sercomm/README.txt
new file mode 100644
index 000000000..a9239a204
--- /dev/null
+++ b/nuttx/drivers/sercomm/README.txt
@@ -0,0 +1,19 @@
+drivers/sercomm README
+======================
+
+If CONFIG_SERCOMM_CONSOLE is defined in the NuttX configuration file, NuttX
+will attempt to use sercomm (HDLC protocol) to communicate with the
+host system. Sercomm is the transport used by osmocom-bb that runs on top
+of serial. See http://bb.osmocom.org/trac/wiki/nuttx-bb/run for detailed
+the usage of nuttx with sercomm.
+
+The drivers/sercomm build that you have the osmocom-bb project directory
+at same level as the nuttx project:
+
+ |- nuttx
+ |- apps
+ `- osmocom-bb
+
+If you attempt to build this driver without osmocom-bb, you will get
+compilation errors because ofheader files that are needed from the
+osmocom-bb directory.
diff --git a/nuttx/drivers/sercomm/console.c b/nuttx/drivers/sercomm/console.c
new file mode 100644
index 000000000..3d038af7c
--- /dev/null
+++ b/nuttx/drivers/sercomm/console.c
@@ -0,0 +1,182 @@
+/****************************************************************************
+ * drivers/sercomm/console.c
+ * Driver for NuttX Console
+ *
+ * (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2011 Stefan Richter <ichgeh@l--putt.de>
+ *
+ * This source code is derivated from Osmocom-BB project and was
+ * relicensed as BSD with permission from original authors.
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <nuttx/config.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/serial/serial.h>
+
+#include <errno.h>
+#include <debug.h>
+#include <string.h>
+
+#include "uart.h"
+#include <nuttx/sercomm/sercomm.h>
+
+/* stubs to make serial driver happy */
+void sercomm_recvchars(void *a) { }
+void sercomm_xmitchars(void *a) { }
+
+/* Stubs to make memory allocator happy */
+void cons_puts(void *foo){}
+void delay_ms(int ms){}
+
+/************************************************************************************
+ * Fileops Prototypes and Structures
+ ************************************************************************************/
+
+typedef FAR struct file file_t;
+
+static ssize_t sc_console_read(file_t *filep, FAR char *buffer, size_t buflen);
+static ssize_t sc_console_write(file_t *filep, FAR const char *buffer, size_t buflen);
+static int sc_console_ioctl(file_t *filep, int cmd, unsigned long arg);
+#ifndef CONFIG_DISABLE_POLL
+static int sc_console_poll(file_t *filep, FAR struct pollfd *fds, bool setup);
+#endif
+
+static const struct file_operations g_sercom_console_ops =
+{
+ 0, /* open, always opened */
+ 0, /* close, stays open */
+ sc_console_read, /* read */
+ sc_console_write, /* write */
+ 0, /* seek, not supported */
+ sc_console_ioctl, /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ sc_console_poll /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Helper functions
+ ****************************************************************************/
+static FAR uart_dev_t *readdev = NULL;
+static struct msgb *recvmsg = NULL;
+static void recv_cb(uint8_t dlci, struct msgb *msg)
+{
+ sem_post(&readdev->recvsem);
+ recvmsg = msg;
+}
+
+/****************************************************************************
+ * Fileops
+ ****************************************************************************/
+
+/* XXX: recvmsg is overwritten when multiple msg arrive! */
+static ssize_t sc_console_read(file_t *filep, FAR char *buffer, size_t buflen)
+{
+ size_t len;
+ struct msgb *tmp;
+
+ /* Wait until data is received */
+ while(recvmsg == NULL) {
+ sem_wait(&readdev->recvsem);
+ }
+
+ len = recvmsg->len > buflen ? buflen : recvmsg->len;
+ memcpy(buffer, msgb_get(recvmsg, len), len);
+
+ if(recvmsg->len == 0) {
+ /* prevent inconsistent msg by first invalidating it, then free it */
+ tmp = recvmsg;
+ recvmsg = NULL;
+ msgb_free(tmp);
+ }
+
+ return len;
+}
+
+/* XXX: redirect to old Osmocom-BB comm/sercomm_cons.c -> 2 buffers */
+extern int sercomm_puts(const char *s);
+static ssize_t sc_console_write(file_t *filep, FAR const char *buffer, size_t buflen)
+{
+ int i, cnt;
+ char dstbuf[32];
+
+ if (buflen >= 31)
+ cnt = 31;
+ else
+ cnt = buflen;
+
+ memcpy(dstbuf, buffer, cnt);
+ dstbuf[cnt] = '\0';
+
+ /* print part of our buffer */
+ sercomm_puts(dstbuf);
+
+ /* wait a little bit to get data transfered */
+ up_mdelay(1);
+
+ return cnt;
+}
+
+/* Forward ioctl to uart driver */
+static int sc_console_ioctl(struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR uart_dev_t *dev = inode->i_private;
+
+ return dev->ops->ioctl(filep, cmd, arg);
+}
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/* Use sercomm on uart driver, register console driver */
+int sercomm_register(FAR const char *path, FAR uart_dev_t *dev)
+{
+ /* XXX: initialize MODEMUART to be used for sercomm*/
+ uart_init(SERCOMM_UART_NR, 1);
+ uart_baudrate(SERCOMM_UART_NR, UART_115200);
+ readdev = dev;
+ sercomm_register_rx_cb(SC_DLCI_LOADER, &recv_cb);
+
+ sem_init(&dev->xmit.sem, 0, 1);
+ sem_init(&dev->recv.sem, 0, 1);
+ sem_init(&dev->closesem, 0, 1);
+ sem_init(&dev->xmitsem, 0, 0);
+ sem_init(&dev->recvsem, 0, 0);
+#ifndef CONFIG_DISABLE_POLL
+ sem_init(&dev->pollsem, 0, 1);
+#endif
+
+ dbg("Registering %s\n", path);
+ return register_driver(path, &g_sercom_console_ops, 0666, NULL);
+}
diff --git a/nuttx/drivers/sercomm/loadwriter.py b/nuttx/drivers/sercomm/loadwriter.py
new file mode 100644
index 000000000..6234d6f0d
--- /dev/null
+++ b/nuttx/drivers/sercomm/loadwriter.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+from socket import *
+import time
+
+SOCKET_NAME = '/tmp/osmocom_loader'
+
+s = socket(AF_UNIX, SOCK_STREAM)
+s.connect(SOCKET_NAME)
+
+while 1:
+ try:
+ x = raw_input(">")
+ y = len(x) + 1
+ s.send(chr(y>>8) + chr(y&255) + x + "\n")
+ except:
+ print ''
+ break
+
+s.close()
diff --git a/nuttx/drivers/sercomm/uart.c b/nuttx/drivers/sercomm/uart.c
new file mode 100644
index 000000000..691bba9ec
--- /dev/null
+++ b/nuttx/drivers/sercomm/uart.c
@@ -0,0 +1,469 @@
+/****************************************************************************
+ * drivers/sercomm/uart.c
+ * Calypso DBB internal UART Driver
+ *
+ * (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <nuttx/config.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <arch/calypso/memory.h>
+#include <arch/calypso/debug.h>
+
+#include <arch/calypso/defines.h>
+//#include <arch/calypso/console.h>
+#include <nuttx/sercomm/sercomm.h>
+
+#include "uart.h"
+
+#define BASE_ADDR_UART_MODEM 0xffff5000
+#define OFFSET_IRDA 0x800
+
+#define UART_REG(n,m) (BASE_ADDR_UART_MODEM + ((n)*OFFSET_IRDA)+(m))
+
+#define LCR7BIT 0x80
+#define LCRBFBIT 0x40
+#define MCR6BIT 0x20
+#define REG_OFFS(m) ((m) & ~(LCR7BIT|LCRBFBIT|MCR6BIT))
+/* read access LCR[7] = 0 */
+enum uart_reg {
+ RHR = 0,
+ IER = 1,
+ IIR = 2,
+ LCR = 3,
+ MCR = 4,
+ LSR = 5,
+ MSR = 6,
+ SPR = 7,
+ MDR1 = 8,
+ DMR2 = 9,
+ SFLSR = 0x0a,
+ RESUME = 0x0b,
+ SFREGL = 0x0c,
+ SFREGH = 0x0d,
+ BLR = 0x0e,
+ ACREG = 0x0f,
+ SCR = 0x10,
+ SSR = 0x11,
+ EBLR = 0x12,
+/* read access LCR[7] = 1 */
+ DLL = RHR | LCR7BIT,
+ DLH = IER | LCR7BIT,
+ DIV1_6 = ACREG | LCR7BIT,
+/* read/write access LCR[7:0] = 0xbf */
+ EFR = IIR | LCRBFBIT,
+ XON1 = MCR | LCRBFBIT,
+ XON2 = LSR | LCRBFBIT,
+ XOFF1 = MSR | LCRBFBIT,
+ XOFF2 = SPR | LCRBFBIT,
+/* read/write access if EFR[4] = 1 and MCR[6] = 1 */
+ TCR = MSR | MCR6BIT,
+ TLR = SPR | MCR6BIT,
+};
+/* write access LCR[7] = 0 */
+#define THR RHR
+#define FCR IIR /* only if EFR[4] = 1 */
+#define TXFLL SFLSR
+#define TXFLH RESUME
+#define RXFLL SFREGL
+#define RXFLH SFREGH
+
+enum fcr_bits {
+ FIFO_EN = (1 << 0),
+ RX_FIFO_CLEAR = (1 << 1),
+ TX_FIFO_CLEAR = (1 << 2),
+ DMA_MODE = (1 << 3),
+};
+#define TX_FIFO_TRIG_SHIFT 4
+#define RX_FIFO_TRIG_SHIFT 6
+
+enum iir_bits {
+ IIR_INT_PENDING = 0x01,
+ IIR_INT_TYPE = 0x3E,
+ IIR_INT_TYPE_RX_STATUS_ERROR = 0x06,
+ IIR_INT_TYPE_RX_TIMEOUT = 0x0C,
+ IIR_INT_TYPE_RHR = 0x04,
+ IIR_INT_TYPE_THR = 0x02,
+ IIR_INT_TYPE_MSR = 0x00,
+ IIR_INT_TYPE_XOFF = 0x10,
+ IIR_INT_TYPE_FLOW = 0x20,
+ IIR_FCR0_MIRROR = 0xC0,
+};
+
+#define UART_REG_UIR 0xffff6000
+
+/* enable or disable the divisor latch for access to DLL, DLH */
+static void uart_set_lcr7bit(int uart, int on)
+{
+ uint8_t reg;
+
+ reg = readb(UART_REG(uart, LCR));
+ if (on)
+ reg |= (1 << 7);
+ else
+ reg &= ~(1 << 7);
+ writeb(reg, UART_REG(uart, LCR));
+}
+
+static uint8_t old_lcr;
+static void uart_set_lcr_bf(int uart, int on)
+{
+ if (on) {
+ old_lcr = readb(UART_REG(uart, LCR));
+ writeb(0xBF, UART_REG(uart, LCR));
+ } else {
+ writeb(old_lcr, UART_REG(uart, LCR));
+ }
+}
+
+/* Enable or disable the TCR_TLR latch bit in MCR[6] */
+static void uart_set_mcr6bit(int uart, int on)
+{
+ uint8_t mcr;
+ /* we assume EFR[4] is always set to 1 */
+ mcr = readb(UART_REG(uart, MCR));
+ if (on)
+ mcr |= (1 << 6);
+ else
+ mcr &= ~(1 << 6);
+ writeb(mcr, UART_REG(uart, MCR));
+}
+
+static void uart_reg_write(int uart, enum uart_reg reg, uint8_t val)
+{
+ if (reg & LCRBFBIT)
+ uart_set_lcr_bf(uart, 1);
+ else if (reg & LCR7BIT)
+ uart_set_lcr7bit(uart, 1);
+ else if (reg & MCR6BIT)
+ uart_set_mcr6bit(uart, 1);
+
+ writeb(val, UART_REG(uart, REG_OFFS(reg)));
+
+ if (reg & LCRBFBIT)
+ uart_set_lcr_bf(uart, 0);
+ else if (reg & LCR7BIT)
+ uart_set_lcr7bit(uart, 0);
+ else if (reg & MCR6BIT)
+ uart_set_mcr6bit(uart, 0);
+}
+
+/* read from a UART register, applying any required latch bits */
+static uint8_t uart_reg_read(int uart, enum uart_reg reg)
+{
+ uint8_t ret;
+
+ if (reg & LCRBFBIT)
+ uart_set_lcr_bf(uart, 1);
+ else if (reg & LCR7BIT)
+ uart_set_lcr7bit(uart, 1);
+ else if (reg & MCR6BIT)
+ uart_set_mcr6bit(uart, 1);
+
+ ret = readb(UART_REG(uart, REG_OFFS(reg)));
+
+ if (reg & LCRBFBIT)
+ uart_set_lcr_bf(uart, 0);
+ else if (reg & LCR7BIT)
+ uart_set_lcr7bit(uart, 0);
+ else if (reg & MCR6BIT)
+ uart_set_mcr6bit(uart, 0);
+
+ return ret;
+}
+
+#if 0
+static void uart_irq_handler_cons(__unused enum irq_nr irqnr)
+{
+ const uint8_t uart = CONS_UART_NR;
+ uint8_t iir;
+
+ //uart_putchar_nb(uart, 'U');
+
+ iir = uart_reg_read(uart, IIR);
+ if (iir & IIR_INT_PENDING)
+ return;
+
+ switch (iir & IIR_INT_TYPE) {
+ case IIR_INT_TYPE_RHR:
+ break;
+ case IIR_INT_TYPE_THR:
+ if (cons_rb_flush() == 1) {
+ /* everything was flushed, disable THR IRQ */
+ uint8_t ier = uart_reg_read(uart, IER);
+ ier &= ~(1 << 1);
+ uart_reg_write(uart, IER, ier);
+ }
+ break;
+ case IIR_INT_TYPE_MSR:
+ break;
+ case IIR_INT_TYPE_RX_STATUS_ERROR:
+ break;
+ case IIR_INT_TYPE_RX_TIMEOUT:
+ break;
+ case IIR_INT_TYPE_XOFF:
+ break;
+ }
+}
+#endif
+
+static void uart_irq_handler_sercomm(__unused enum irq_nr irqnr, __unused void *context)
+{
+ const uint8_t uart = SERCOMM_UART_NR;
+ uint8_t iir, ch;
+
+ //uart_putchar_nb(uart, 'U');
+
+ iir = uart_reg_read(uart, IIR);
+ if (iir & IIR_INT_PENDING)
+ return;
+
+ switch (iir & IIR_INT_TYPE) {
+ case IIR_INT_TYPE_RX_TIMEOUT:
+ case IIR_INT_TYPE_RHR:
+ /* as long as we have rx data available */
+ while (uart_getchar_nb(uart, &ch)) {
+ if (sercomm_drv_rx_char(ch) < 0) {
+ /* sercomm cannot receive more data right now */
+ uart_irq_enable(uart, UART_IRQ_RX_CHAR, 0);
+ }
+ }
+ break;
+ case IIR_INT_TYPE_THR:
+ /* as long as we have space in the FIFO */
+ while (!uart_tx_busy(uart)) {
+ /* get a byte from sercomm */
+ if (!sercomm_drv_pull(&ch)) {
+ /* no more bytes in sercomm, stop TX interrupts */
+ uart_irq_enable(uart, UART_IRQ_TX_EMPTY, 0);
+ break;
+ }
+ /* write the byte into the TX FIFO */
+ uart_putchar_nb(uart, ch);
+ }
+ break;
+ case IIR_INT_TYPE_MSR:
+ printf("UART IRQ MSR\n");
+ break;
+ case IIR_INT_TYPE_RX_STATUS_ERROR:
+ printf("UART IRQ RX_SE\n");
+ break;
+ case IIR_INT_TYPE_XOFF:
+ printf("UART IRQXOFF\n");
+ break;
+ }
+}
+
+static const uint8_t uart2irq[] = {
+ [0] = IRQ_UART_IRDA,
+ [1] = IRQ_UART_MODEM,
+};
+
+void uart_init(uint8_t uart, uint8_t interrupts)
+{
+ uint8_t irq = uart2irq[uart];
+
+ uart_reg_write(uart, IER, 0x00);
+
+ if (uart == SERCOMM_UART_NR) {
+ sercomm_init();
+ irq_attach(IRQ_UART_MODEM, (xcpt_t)uart_irq_handler_sercomm);
+ up_enable_irq(IRQ_UART_MODEM);
+ uart_irq_enable(uart, UART_IRQ_RX_CHAR, 1);
+ }
+
+#if 0
+ if (uart == CONS_UART_NR) {
+ cons_init();
+ if(interrupts) {
+ irq_register_handler(irq, &uart_irq_handler_cons);
+ irq_config(irq, 0, 0, 0xff);
+ irq_enable(irq);
+ }
+ } else {
+ sercomm_init();
+ if(interrupts) {
+ irq_register_handler(irq, &uart_irq_handler_sercomm);
+ irq_config(irq, 0, 0, 0xff);
+ irq_enable(irq);
+ }
+ uart_irq_enable(uart, UART_IRQ_RX_CHAR, 1);
+ }
+#endif
+#if 0
+ if (uart == 1) {
+ /* assign UART to MCU and unmask interrupts*/
+ writeb(UART_REG_UIR, 0x00);
+ }
+#endif
+
+ /* if we don't initialize these, we get strange corruptions in the
+ received data... :-( */
+ uart_reg_write(uart, MDR1, 0x07); /* turn off UART */
+ uart_reg_write(uart, XON1, 0x00); /* Xon1/Addr Register */
+ uart_reg_write(uart, XON2, 0x00); /* Xon2/Addr Register */
+ uart_reg_write(uart, XOFF1, 0x00); /* Xoff1 Register */
+ uart_reg_write(uart, XOFF2, 0x00); /* Xoff2 Register */
+ uart_reg_write(uart, EFR, 0x00); /* Enhanced Features Register */
+
+ /* select UART mode */
+ uart_reg_write(uart, MDR1, 0);
+ /* no XON/XOFF flow control, ENHANCED_EN, no auto-RTS/CTS */
+ uart_reg_write(uart, EFR, (1 << 4));
+ /* enable Tx/Rx FIFO, Tx trigger at 56 spaces, Rx trigger at 60 chars */
+ uart_reg_write(uart, FCR, FIFO_EN | RX_FIFO_CLEAR | TX_FIFO_CLEAR |
+ (3 << TX_FIFO_TRIG_SHIFT) | (3 << RX_FIFO_TRIG_SHIFT));
+
+ /* THR interrupt only when TX FIFO and TX shift register are empty */
+ uart_reg_write(uart, SCR, (1 << 0));// | (1 << 3));
+
+ /* 8 bit, 1 stop bit, no parity, no break */
+ uart_reg_write(uart, LCR, 0x03);
+
+ uart_set_lcr7bit(uart, 0);
+}
+
+void uart_poll(uint8_t uart) {
+/* if(uart == CONS_UART_NR) {
+ uart_irq_handler_cons(0);
+ } else
+*/ {
+ uart_irq_handler_sercomm(0, NULL);
+ }
+}
+
+void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on)
+{
+ uint8_t ier = uart_reg_read(uart, IER);
+ uint8_t mask = 0;
+
+ switch (irq) {
+ case UART_IRQ_TX_EMPTY:
+ mask = (1 << 1);
+ break;
+ case UART_IRQ_RX_CHAR:
+ mask = (1 << 0);
+ break;
+ }
+
+ if (on)
+ ier |= mask;
+ else
+ ier &= ~mask;
+
+ uart_reg_write(uart, IER, ier);
+}
+
+
+void uart_putchar_wait(uint8_t uart, int c)
+{
+ /* wait while TX FIFO indicates full */
+ while (readb(UART_REG(uart, SSR)) & 0x01) { }
+
+ /* put character in TX FIFO */
+ writeb(c, UART_REG(uart, THR));
+}
+
+int uart_putchar_nb(uint8_t uart, int c)
+{
+ /* if TX FIFO indicates full, abort */
+ if (readb(UART_REG(uart, SSR)) & 0x01)
+ return 0;
+
+ writeb(c, UART_REG(uart, THR));
+ return 1;
+}
+
+int uart_getchar_nb(uint8_t uart, uint8_t *ch)
+{
+ uint8_t lsr;
+
+ lsr = readb(UART_REG(uart, LSR));
+
+ /* something strange happened */
+ if (lsr & 0x02)
+ printf("LSR RX_OE\n");
+ if (lsr & 0x04)
+ printf("LSR RX_PE\n");
+ if (lsr & 0x08)
+ printf("LSR RX_FE\n");
+ if (lsr & 0x10)
+ printf("LSR RX_BI\n");
+ if (lsr & 0x80)
+ printf("LSR RX_FIFO_STS\n");
+
+ /* is the Rx FIFO empty? */
+ if (!(lsr & 0x01))
+ return 0;
+
+ *ch = readb(UART_REG(uart, RHR));
+ //printf("getchar_nb(%u) = %02x\n", uart, *ch);
+ return 1;
+}
+
+int uart_tx_busy(uint8_t uart)
+{
+ if (readb(UART_REG(uart, SSR)) & 0x01)
+ return 1;
+ return 0;
+}
+
+static const uint16_t divider[] = {
+ [UART_38400] = 21, /* 38,690 */
+ [UART_57600] = 14, /* 58,035 */
+ [UART_115200] = 7, /* 116,071 */
+ [UART_230400] = 4, /* 203,125! (-3% would be 223,488) */
+ [UART_460800] = 2, /* 406,250! (-3% would be 446,976) */
+ [UART_921600] = 1, /* 812,500! (-3% would be 893,952) */
+};
+
+int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt)
+{
+ uint16_t div;
+
+ if (bdrt > ARRAY_SIZE(divider))
+ return -1;
+
+ div = divider[bdrt];
+ uart_set_lcr7bit(uart, 1);
+ writeb(div & 0xff, UART_REG(uart, DLL));
+ writeb(div >> 8, UART_REG(uart, DLH));
+ uart_set_lcr7bit(uart, 0);
+
+ return 0;
+}
diff --git a/nuttx/drivers/sercomm/uart.h b/nuttx/drivers/sercomm/uart.h
new file mode 100644
index 000000000..81d7a1560
--- /dev/null
+++ b/nuttx/drivers/sercomm/uart.h
@@ -0,0 +1,32 @@
+#ifndef _UART_H
+#define _UART_H
+
+#include <stdint.h>
+
+enum uart_baudrate {
+ UART_38400,
+ UART_57600,
+ UART_115200,
+ UART_230400,
+ UART_460800,
+ UART_614400,
+ UART_921600,
+};
+
+void uart_init(uint8_t uart, uint8_t interrupts);
+void uart_putchar_wait(uint8_t uart, int c);
+int uart_putchar_nb(uint8_t uart, int c);
+int uart_getchar_nb(uint8_t uart, uint8_t *ch);
+int uart_tx_busy(uint8_t uart);
+int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt);
+
+enum uart_irq {
+ UART_IRQ_TX_EMPTY,
+ UART_IRQ_RX_CHAR,
+};
+
+void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on);
+
+void uart_poll(uint8_t uart);
+
+#endif /* _UART_H */
diff --git a/nuttx/drivers/serial/Kconfig b/nuttx/drivers/serial/Kconfig
new file mode 100644
index 000000000..43869fdec
--- /dev/null
+++ b/nuttx/drivers/serial/Kconfig
@@ -0,0 +1,1049 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config LOWLEVEL_CONSOLE
+ bool "Low-level console support"
+ default n
+ depends on ARCH_LOWPUTC
+
+config 16550_UART
+ bool "16550 UART Chip support"
+ default n
+
+if 16550_UART
+config 16550_UART0
+ bool "16550 UART0"
+ default n
+
+if 16550_UART0
+config 16550_UART0_BASE
+ hex "16550 UART0 base address"
+
+config 16550_UART0_CLOCK
+ int "16550 UART0 clock"
+
+config 16550_UART0_IRQ
+ int "16550 UART0 IRQ number"
+
+config 16550_UART0_BAUD
+ int "16550 UART0 BAUD"
+ default 115200
+
+config 16550_UART0_PARITY
+ int "16550 UART0 parity"
+ default 0
+ ---help---
+ 16550 UART0 parity. 0=None, 1=Odd, 2=Even. Default: None
+
+config 16550_UART0_BITS
+ int "16550 UART0 number of bits"
+ default 8
+ ---help---
+ 16550 UART0 number of bits. Default: 8
+
+config 16550_UART0_2STOP
+ int "16550 UART0 two stop bits"
+ default 0
+ ---help---
+ 0=1 stop bit, 1=Two stop bits. Default: 1 stop bit
+
+config 16550_UART0_RXBUFSIZE
+ int "16550 UART0 Rx buffer size"
+ default 256
+ ---help---
+ 16550 UART0 Rx buffer size. Default: 256
+
+config 16550_UART0_TXBUFSIZE
+ int "16550 UART0 Tx buffer size"
+ default 256
+ ---help---
+ 16550 UART0 Tx buffer size. Default: 256
+
+endif
+
+config 16550_UART1
+ bool "16550 UART1"
+ default n
+
+if 16550_UART1
+config 16550_UART1_BASE
+ hex "16550 UART1 base address"
+
+config 16550_UART1_CLOCK
+ int "16550 UART1 clock"
+
+config 16550_UART1_IRQ
+ int "16550 UART1 IRQ number"
+
+config 16550_UART1_BAUD
+ int "16550 UART1 BAUD"
+ default 115200
+
+config 16550_UART1_PARITY
+ int "16550 UART1 parity"
+ default 0
+ ---help---
+ 16550 UART1 parity. 0=None, 1=Odd, 2=Even. Default: None
+
+config 16550_UART1_BITS
+ int "16550 UART1 number of bits"
+ default 8
+ ---help---
+ 16550 UART1 number of bits. Default: 8
+
+config 16550_UART1_2STOP
+ int "16550 UART1 two stop bits"
+ default 0
+ ---help---
+ 0=1 stop bit, 1=Two stop bits. Default: 1 stop bit
+
+config 16550_UART1_RXBUFSIZE
+ int "16550 UART1 Rx buffer size"
+ default 256
+ ---help---
+ 16550 UART1 Rx buffer size. Default: 256
+
+config 16550_UART1_TXBUFSIZE
+ int "16550 UART1 Tx buffer size"
+ default 256
+ ---help---
+ 16550 UART1 Tx buffer size. Default: 256
+
+endif
+
+config 16550_UART2
+ bool "16550 UART2"
+ default n
+
+if 16550_UART2
+config 16550_UART2_BASE
+ hex "16550 UART2 base address"
+
+config 16550_UART2_CLOCK
+ int "16550 UART2 clock"
+
+config 16550_UART2_IRQ
+ int "16550 UART2 IRQ number"
+
+config 16550_UART2_BAUD
+ int "16550 UART2 BAUD"
+ default 115200
+
+config 16550_UART2_PARITY
+ int "16550 UART2 parity"
+ default 0
+ ---help---
+ 16550 UART2 parity. 0=None, 1=Odd, 2=Even. Default: None
+
+config 16550_UART2_BITS
+ int "16550 UART2 number of bits"
+ default 8
+ ---help---
+ 16550 UART2 number of bits. Default: 8
+
+config 16550_UART2_2STOP
+ int "16550 UART2 two stop bits"
+ default 0
+ ---help---
+ 0=1 stop bit, 1=Two stop bits. Default: 1 stop bit
+
+config 16550_UART2_RXBUFSIZE
+ int "16550 UART2 Rx buffer size"
+ default 256
+ ---help---
+ 16550 UART2 Rx buffer size. Default: 256
+
+config 16550_UART2_TXBUFSIZE
+ int "16550 UART2 Tx buffer size"
+ default 256
+ ---help---
+ 16550 UART2 Tx buffer size. Default: 256
+
+endif
+
+config 16550_UART3
+ bool "16550 UART3"
+ default n
+
+if 16550_UART3
+config 16550_UART3_BASE
+ hex "16550 UART3 base address"
+
+config 16550_UART3_CLOCK
+ int "16550 UART3 clock"
+
+config 16550_UART3_IRQ
+ int "16550 UART3 IRQ number"
+
+config 16550_UART3_BAUD
+ int "16550 UART3 BAUD"
+ default 115200
+
+config 16550_UART3_PARITY
+ int "16550 UART3 parity"
+ default 0
+ ---help---
+ 16550 UART3 parity. 0=None, 1=Odd, 2=Even. Default: None
+
+config 16550_UART3_BITS
+ int "16550 UART3 number of bits"
+ default 8
+ ---help---
+ 16550 UART3 number of bits. Default: 8
+
+config 16550_UART3_2STOP
+ int "16550 UART3 two stop bits"
+ default 0
+ ---help---
+ 0=1 stop bit, 1=Two stop bits. Default: 1 stop bit
+
+config 16550_UART3_RXBUFSIZE
+ int "16550 UART3 Rx buffer size"
+ default 256
+ ---help---
+ 16550 UART3 Rx buffer size. Default: 256
+
+config 16550_UART3_TXBUFSIZE
+ int "16550 UART3 Tx buffer size"
+ default 256
+ ---help---
+ 16550 UART3 Tx buffer size. Default: 256
+
+endif
+
+choice
+ prompt "16550 Serial Console"
+ default 16550_NO_SERIAL_CONSOLE
+
+config 16550_UART0_SERIAL_CONSOLE
+ bool "16550 UART0 serial console"
+ depends on 16550_UART0
+
+config 16550_UART1_SERIAL_CONSOLE
+ bool "16550 UART1 serial console"
+ depends on 16550_UART1
+
+config 16550_UART2_SERIAL_CONSOLE
+ bool "16550 UART2 serial console"
+ depends on 16550_UART2
+
+config 16550_UART3_SERIAL_CONSOLE
+ bool "16550 UART3 serial console"
+ depends on 16550_UART3
+
+config 16550_NO_SERIAL_CONSOLE
+ bool "No 16550 serial console"
+
+endchoice
+
+config 16550_SUPRESS_CONFIG
+ bool "Suppress 16550 configuration"
+ default n
+ ---help---
+ This option is useful, for example, if you are using a bootloader
+ that configures the 16550_UART. In that case, you may want to
+ just leave the existing console configuration in place. Default: n
+
+config 16550_REGINCR
+ int "Address increment between 16550 registers"
+ default 1
+ ---help---
+ The address increment between 16550 registers. Options are 1, 2, or 4.
+ Default: 1
+
+config 16550_REGWIDTH
+ int "Bit width of 16550 registers"
+ default 8
+ ---help---
+ The bit width of registers. Options are 8, 16, or 32. Default: 8
+
+config 16550_ADDRWIDTH
+ int "Address width of 16550 registers"
+ default 8
+ ---help---
+ The bit width of registers. Options are 8, 16, or 32. Default: 8
+
+endif
+
+#
+# MCU serial peripheral driver?
+#
+
+config ARCH_HAVE_UART
+ bool
+config ARCH_HAVE_UART0
+ bool
+config ARCH_HAVE_UART1
+ bool
+config ARCH_HAVE_UART2
+ bool
+config ARCH_HAVE_UART3
+ bool
+config ARCH_HAVE_UART4
+ bool
+config ARCH_HAVE_UART5
+ bool
+config ARCH_HAVE_UART6
+ bool
+
+config ARCH_HAVE_USART0
+ bool
+config ARCH_HAVE_USART1
+ bool
+config ARCH_HAVE_USART2
+ bool
+config ARCH_HAVE_USART3
+ bool
+config ARCH_HAVE_USART4
+ bool
+config ARCH_HAVE_USART5
+ bool
+config ARCH_HAVE_USART6
+ bool
+
+config MCU_SERIAL
+ bool
+ default y if ARCH_HAVE_UART || ARCH_HAVE_UART0 || ARCH_HAVE_USART0 || ARCH_HAVE_UART1 || ARCH_HAVE_USART1 || \
+ ARCH_HAVE_UART2 || ARCH_HAVE_USART2 || ARCH_HAVE_UART3 || ARCH_HAVE_USART3 || \
+ ARCH_HAVE_UART4 || ARCH_HAVE_USART4 || ARCH_HAVE_UART5 || ARCH_HAVE_USART5 || ARCH_HAVE_UART6 || ARCH_HAVE_USART6
+
+#
+# Standard serial driver configuration
+#
+
+config STANDARD_SERIAL
+ bool "Enable standard \"upper-half\" serial driver"
+ default y if MCU_SERIAL
+ default n if !MCU_SERIAL
+ depends on !LOWLEVEL_CONSOLE
+ ---help---
+ Enable the standard, upper-half serial driver used by most MCU serial peripherals.
+
+config CONFIG_SERIAL_NPOLLWAITERS
+ int "Number of poll threads"
+ default 2
+ depends on !DISABLE_POLL && STANDARD_SERIAL
+ ---help---
+ Maximum number of threads than can be waiting for POLL events.
+ Default: 2
+
+#
+# U[S]ARTn_XYZ settings for MCU serial drivers
+#
+
+choice
+ prompt "Serial console"
+ depends on MCU_SERIAL
+ default NO_SERIAL_CONSOLE
+
+config UART_SERIAL_CONSOLE
+ bool "UART"
+ depends on ARCH_HAVE_UART
+
+config UART0_SERIAL_CONSOLE
+ bool "UART0"
+ depends on ARCH_HAVE_UART0
+
+config USART0_SERIAL_CONSOLE
+ bool "USART0"
+ depends on ARCH_HAVE_USART0
+
+config UART1_SERIAL_CONSOLE
+ bool "UART1"
+ depends on ARCH_HAVE_UART1
+
+config USART1_SERIAL_CONSOLE
+ bool "USART1"
+ depends on ARCH_HAVE_USART1
+
+config UART2_SERIAL_CONSOLE
+ bool "UART2"
+ depends on ARCH_HAVE_UART2
+
+config USART2_SERIAL_CONSOLE
+ bool "USART2"
+ depends on ARCH_HAVE_USART2
+
+config UART3_SERIAL_CONSOLE
+ bool "UART3"
+ depends on ARCH_HAVE_UART3
+
+config USART3_SERIAL_CONSOLE
+ bool "USART3"
+ depends on ARCH_HAVE_USART3
+
+config UART4_SERIAL_CONSOLE
+ bool "UART4"
+ depends on ARCH_HAVE_UART4
+
+config USART4_SERIAL_CONSOLE
+ bool "USART4"
+ depends on ARCH_HAVE_USART4
+
+config UART5_SERIAL_CONSOLE
+ bool "UART5"
+ depends on ARCH_HAVE_UART5
+
+config USART5_SERIAL_CONSOLE
+ bool "USART5"
+ depends on ARCH_HAVE_USART5
+
+config UART6_SERIAL_CONSOLE
+ bool "UART6"
+ depends on ARCH_HAVE_UART6
+
+config USART6_SERIAL_CONSOLE
+ bool "USART6"
+ depends on ARCH_HAVE_USART6
+
+config NO_SERIAL_CONSOLE
+ bool "No serial console"
+
+endchoice
+
+menu "UART Configuration"
+ depends on ARCH_HAVE_UART
+
+config UART_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config UART_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config UART_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the UART.
+
+config UART_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config UART_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config UART_2STOP
+ int "use 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "UART0 Configuration"
+ depends on ARCH_HAVE_UART0
+
+config UART0_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config UART0_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config UART0_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the UART.
+
+config UART0_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config UART0_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config UART0_2STOP
+ int "use 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "USART0 Configuration"
+ depends on ARCH_HAVE_USART0
+
+config USART0_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config USART0_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config USART0_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the USART.
+
+config USART0_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config USART0_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config USART0_2STOP
+ int "use 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "UART1 Configuration"
+ depends on ARCH_HAVE_UART1
+
+config UART1_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config UART1_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config UART1_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the UART.
+
+config UART1_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config UART1_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config UART1_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "USART1 Configuration"
+ depends on ARCH_HAVE_USART1
+
+config USART1_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config USART1_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config USART1_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the USART.
+
+config USART1_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config USART1_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config USART1_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "UART2 Configuration"
+ depends on ARCH_HAVE_UART2
+
+config UART2_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config UART2_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config UART2_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the UART.
+
+config UART2_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config UART2_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config UART2_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "USART2 Configuration"
+ depends on ARCH_HAVE_USART2
+
+config USART2_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config USART2_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config USART2_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the USART.
+
+config USART2_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config USART2_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config USART2_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "UART3 Configuration"
+ depends on ARCH_HAVE_UART3
+
+config UART3_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config UART3_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config UART3_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the UART.
+
+config UART3_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config UART3_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config UART3_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "USART3 Configuration"
+ depends on ARCH_HAVE_USART3
+
+config USART3_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config USART3_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config USART3_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the USART.
+
+config USART3_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config USART3_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config USART3_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "UART4 Configuration"
+ depends on ARCH_HAVE_UART4
+
+config UART4_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config UART4_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config UART4_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the UART.
+
+config UART4_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config UART4_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config UART4_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "USART4 Configuration"
+ depends on ARCH_HAVE_USART4
+
+config USART4_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config USART4_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config USART4_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the USART.
+
+config USART4_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config USART4_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config USART4_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "UART5 Configuration"
+ depends on ARCH_HAVE_UART5
+
+config UART5_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config UART5_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config UART5_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the UART.
+
+config UART5_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config UART5_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config UART5_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "USART5 Configuration"
+ depends on ARCH_HAVE_USART5
+
+config USART5_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config USART5_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config USART5_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the USART.
+
+config USART5_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config USART5_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config USART5_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "USART6 Configuration"
+ depends on ARCH_HAVE_USART6
+
+config USART6_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config USART6_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config USART6_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the USART.
+
+config USART6_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config USART6_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config USART6_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
+
+menu "UART6 Configuration"
+ depends on ARCH_HAVE_UART6
+
+config UART6_RXBUFSIZE
+ int "receive buffer size"
+ default 256
+ help
+ Characters are buffered as they are received. This specifies
+ the size of the receive buffer.
+
+config UART6_TXBUFSIZE
+ int "transmit buffer size"
+ default 256
+ help
+ Characters are buffered before being sent. This specifies
+ the size of the transmit buffer.
+
+config UART6_BAUD
+ int "baud rate"
+ default 11520
+ help
+ The configured BAUD of the UART.
+
+config UART6_BITS
+ int "character size"
+ default 8
+ help
+ The number of bits. Must be either 7 or 8.
+
+config UART6_PARITY
+ int "parity setting"
+ default 0
+ help
+ 0=no parity, 1=odd parity, 2=even parity
+
+config UART6_2STOP
+ int "uses 2 stop bits"
+ default 0
+ help
+ 1=Two stop bits
+
+endmenu
diff --git a/nuttx/drivers/serial/Make.defs b/nuttx/drivers/serial/Make.defs
new file mode 100644
index 000000000..b99f4eb36
--- /dev/null
+++ b/nuttx/drivers/serial/Make.defs
@@ -0,0 +1,50 @@
+############################################################################
+# drivers/serial/Make.defs
+#
+# Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+ifneq ($(CONFIG_NFILE_DESCRIPTORS),0)
+
+# Include serial drivers
+
+CSRCS += serial.c serialirq.c lowconsole.c
+
+ifeq ($(CONFIG_16550_UART),y)
+ CSRCS += uart_16550.c
+endif
+
+# Include serial build support
+
+DEPPATH += --dep-path serial
+VPATH += :serial
+endif
diff --git a/nuttx/drivers/serial/lowconsole.c b/nuttx/drivers/serial/lowconsole.c
new file mode 100644
index 000000000..1fac49a57
--- /dev/null
+++ b/nuttx/drivers/serial/lowconsole.c
@@ -0,0 +1,132 @@
+/****************************************************************************
+ * drivers/serial/lowconsole.c
+ *
+ * Copyright (C) 2008-2009, 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/fs/fs.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* The architecture must provide up_putc for this driver */
+
+#ifndef CONFIG_ARCH_LOWPUTC
+# error "Architecture must provide up_putc() for this driver"
+#endif
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static ssize_t lowconsole_read(struct file *filep, char *buffer, size_t buflen);
+static ssize_t lowconsole_write(struct file *filep, const char *buffer, size_t buflen);
+static int lowconsole_ioctl(struct file *filep, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+static const struct file_operations g_consoleops =
+{
+ 0, /* open */
+ 0, /* close */
+ lowconsole_read, /* read */
+ lowconsole_write, /* write */
+ 0, /* seek */
+ lowconsole_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , 0 /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: lowconsole_ioctl
+ ****************************************************************************/
+
+static int lowconsole_ioctl(struct file *filep, int cmd, unsigned long arg)
+{
+ return -ENOTTY;
+}
+
+/****************************************************************************
+ * Name: lowconsole_read
+ ****************************************************************************/
+
+static ssize_t lowconsole_read(struct file *filep, char *buffer, size_t buflen)
+{
+ return 0;
+}
+
+/****************************************************************************
+ * Name: lowconsole_write
+ ****************************************************************************/
+
+static ssize_t lowconsole_write(struct file *filep, const char *buffer, size_t buflen)
+{
+ ssize_t ret = buflen;
+
+ for (; buflen; buflen--)
+ {
+ up_putc(*buffer++);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: lowconsole_init
+****************************************************************************/
+
+void lowconsole_init(void)
+{
+ (void)register_driver("/dev/console", &g_consoleops, 0666, NULL);
+}
diff --git a/nuttx/drivers/serial/serial.c b/nuttx/drivers/serial/serial.c
new file mode 100644
index 000000000..9ffcd75dc
--- /dev/null
+++ b/nuttx/drivers/serial/serial.c
@@ -0,0 +1,954 @@
+/************************************************************************************
+ * drivers/serial/serial.c
+ *
+ * Copyright (C) 2007-2009, 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ************************************************************************************/
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/serial/serial.h>
+
+/************************************************************************************
+ * Definitions
+ ************************************************************************************/
+
+/* The architecture must provide up_putc for this driver */
+
+#ifndef CONFIG_ARCH_LOWPUTC
+# error "Architecture must provide up_putc() for this driver"
+#endif
+
+#define uart_putc(ch) up_putc(ch)
+
+#define HALF_SECOND_MSEC 500
+#define HALF_SECOND_USEC 500000L
+
+/************************************************************************************
+ * Private Types
+ ************************************************************************************/
+
+/************************************************************************************
+ * Private Function Prototypes
+ ************************************************************************************/
+
+static int uart_open(FAR struct file *filep);
+static int uart_close(FAR struct file *filep);
+static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
+static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+static int uart_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+#ifndef CONFIG_DISABLE_POLL
+static int uart_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup);
+#endif
+
+/************************************************************************************
+ * Private Variables
+ ************************************************************************************/
+
+static const struct file_operations g_serialops =
+{
+ uart_open, /* open */
+ uart_close, /* close */
+ uart_read, /* read */
+ uart_write, /* write */
+ 0, /* seek */
+ uart_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , uart_poll /* poll */
+#endif
+};
+
+/************************************************************************************
+ * Private Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: uart_takesem
+ ************************************************************************************/
+
+static int uart_takesem(FAR sem_t *sem, bool errout)
+{
+ /* Loop, ignoring interrupts, until we have successfully acquired the semaphore */
+
+ while (sem_wait(sem) != OK)
+ {
+ /* The only case that an error should occur here is if the wait was awakened
+ * by a signal.
+ */
+
+ ASSERT(get_errno() == EINTR);
+
+ /* When the signal is received, should we errout? Or should we just continue
+ * waiting until we have the semaphore?
+ */
+
+ if (errout)
+ {
+ return -EINTR;
+ }
+ }
+
+ return OK;
+}
+
+/************************************************************************************
+ * Name: uart_givesem
+ ************************************************************************************/
+
+#define uart_givesem(sem) (void)sem_post(sem)
+
+/****************************************************************************
+ * Name: uart_pollnotify
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static void uart_pollnotify(FAR uart_dev_t *dev, pollevent_t eventset)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_SERIAL_NPOLLWAITERS; i++)
+ {
+ struct pollfd *fds = dev->fds[i];
+ if (fds)
+ {
+ fds->revents |= (fds->events & eventset);
+ if (fds->revents != 0)
+ {
+ fvdbg("Report events: %02x\n", fds->revents);
+ sem_post(fds->sem);
+ }
+ }
+ }
+}
+#else
+# define uart_pollnotify(dev,event)
+#endif
+
+/************************************************************************************
+ * Name: uart_putxmitchar
+ ************************************************************************************/
+
+static int uart_putxmitchar(FAR uart_dev_t *dev, int ch)
+{
+ irqstate_t flags;
+ int nexthead;
+ int ret;
+
+ /* Increment to see what the next head pointer will be. We need to use the "next"
+ * head pointer to determine when the circular buffer would overrun
+ */
+
+ nexthead = dev->xmit.head + 1;
+ if (nexthead >= dev->xmit.size)
+ {
+ nexthead = 0;
+ }
+
+ /* Loop until we are able to add the character to the TX buffer */
+
+ for (;;)
+ {
+ if (nexthead != dev->xmit.tail)
+ {
+ dev->xmit.buffer[dev->xmit.head] = ch;
+ dev->xmit.head = nexthead;
+ return OK;
+ }
+ else
+ {
+ /* Inform the interrupt level logic that we are waiting. This and
+ * the following steps must be atomic.
+ */
+
+ flags = irqsave();
+ dev->xmitwaiting = true;
+
+ /* Wait for some characters to be sent from the buffer with the TX
+ * interrupt enabled. When the TX interrupt is enabled, uart_xmitchars
+ * should execute and remove some of the data from the TX buffer.
+ */
+
+ uart_enabletxint(dev);
+ ret = uart_takesem(&dev->xmitsem, true);
+ uart_disabletxint(dev);
+ irqrestore(flags);
+
+ /* Check if we were awakened by signal. */
+
+ if (ret < 0)
+ {
+ /* A signal received while waiting for the xmit buffer to become
+ * non-full will abort the transfer.
+ */
+
+ return -EINTR;
+ }
+ }
+ }
+
+ /* We won't get here */
+
+ return OK;
+}
+
+/************************************************************************************
+ * Name: uart_irqwrite
+ ************************************************************************************/
+
+static inline ssize_t uart_irqwrite(FAR uart_dev_t *dev, FAR const char *buffer, size_t buflen)
+{
+ ssize_t ret = buflen;
+
+ /* Force each character through the low level interface */
+
+ for (; buflen; buflen--)
+ {
+ int ch = *buffer++;
+
+ /* If this is the console, then we should replace LF with CR-LF */
+
+ if (ch == '\n')
+ {
+ uart_putc('\r');
+ }
+
+ /* Output the character, using the low-level direct UART interfaces */
+
+ uart_putc(ch);
+ }
+
+ return ret;
+}
+
+/************************************************************************************
+ * Name: uart_write
+ ************************************************************************************/
+
+static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR uart_dev_t *dev = inode->i_private;
+ ssize_t nread = buflen;
+ int ret;
+
+ /* We may receive console writes through this path from interrupt handlers and
+ * from debug output in the IDLE task! In these cases, we will need to do things
+ * a little differently.
+ */
+
+ if (up_interrupt_context() || getpid() == 0)
+ {
+ /* up_putc() will be used to generate the output in a busy-wait loop.
+ * up_putc() is only available for the console device.
+ */
+
+ if (dev->isconsole)
+ {
+ irqstate_t flags = irqsave();
+ ret = uart_irqwrite(dev, buffer, buflen);
+ irqrestore(flags);
+ return ret;
+ }
+ else
+ {
+ return -EPERM;
+ }
+ }
+
+ /* Only one user can access dev->xmit.head at a time */
+
+ ret = (ssize_t)uart_takesem(&dev->xmit.sem, true);
+ if (ret < 0)
+ {
+ /* A signal received while waiting for access to the xmit.head will
+ * abort the transfer. After the transfer has started, we are committed
+ * and signals will be ignored.
+ */
+
+ return ret;
+ }
+
+ /* Loop while we still have data to copy to the transmit buffer.
+ * we add data to the head of the buffer; uart_xmitchars takes the
+ * data from the end of the buffer.
+ */
+
+ uart_disabletxint(dev);
+ for (; buflen; buflen--)
+ {
+ int ch = *buffer++;
+
+ /* If this is the console, then we should replace LF with CR-LF */
+
+ ret = OK;
+ if (dev->isconsole && ch == '\n')
+ {
+ ret = uart_putxmitchar(dev, '\r');
+ }
+
+ /* Put the character into the transmit buffer */
+
+ if (ret == OK)
+ {
+ ret = uart_putxmitchar(dev, ch);
+ }
+
+ /* Were we awakened by a signal? That should be the only condition that
+ * uart_putxmitchar() should return an error.
+ */
+
+ if (ret < 0)
+ {
+ /* POSIX requires that we return -1 and errno set if no data was
+ * transferred. Otherwise, we return the number of bytes in the
+ * interrupted transfer.
+ */
+
+ if (buflen < nread)
+ {
+ /* Some data was transferred. Return the number of bytes that were
+ * successfully transferred.
+ */
+
+ nread -= buflen;
+ }
+ else
+ {
+ /* No data was transferred. Return -EINTR. The VFS layer will
+ * set the errno value appropriately).
+ */
+
+ nread = -EINTR;
+ }
+
+ break;
+ }
+ }
+
+ if (dev->xmit.head != dev->xmit.tail)
+ {
+ uart_enabletxint(dev);
+ }
+
+ uart_givesem(&dev->xmit.sem);
+ return nread;
+}
+
+/************************************************************************************
+ * Name: uart_read
+ ************************************************************************************/
+
+static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR uart_dev_t *dev = inode->i_private;
+ irqstate_t flags;
+ ssize_t recvd = 0;
+ int16_t tail;
+ int ret;
+
+ /* Only one user can access dev->recv.tail at a time */
+
+ ret = uart_takesem(&dev->recv.sem, true);
+ if (ret < 0)
+ {
+ /* A signal received while waiting for access to the recv.tail will avort
+ * the transfer. After the transfer has started, we are committed and
+ * signals will be ignored.
+ */
+
+ return ret;
+ }
+
+ /* Loop while we still have data to copy to the receive buffer.
+ * we add data to the head of the buffer; uart_xmitchars takes the
+ * data from the end of the buffer.
+ */
+
+ while (recvd < buflen)
+ {
+ /* Check if there is more data to return in the circular buffer.
+ * NOTE: Rx interrupt handling logic may aynchronously increment
+ * the head index but must not modify the tail index. The tail
+ * index is only modified in this function. Therefore, no
+ * special handshaking is required here.
+ *
+ * The head and tail pointers are 16-bit values. The only time that
+ * the following could be unsafe is if the CPU made two non-atomic
+ * 8-bit accesses to obtain the 16-bit head index.
+ */
+
+ tail = dev->recv.tail;
+ if (dev->recv.head != tail)
+ {
+ /* Take the next character from the tail of the buffer */
+
+ *buffer++ = dev->recv.buffer[tail];
+ recvd++;
+
+ /* Increment the tail index. Most operations are done using the
+ * local variable 'tail' so that the final dev->recv.tail update
+ * is atomic.
+ */
+
+ if (++tail >= dev->recv.size)
+ {
+ tail = 0;
+ }
+
+ dev->recv.tail = tail;
+ }
+
+#ifdef CONFIG_DEV_SERIAL_FULLBLOCKS
+ /* No... then we would have to wait to get receive more data.
+ * If the user has specified the O_NONBLOCK option, then just
+ * return what we have.
+ */
+
+ else if (filep->f_oflags & O_NONBLOCK)
+ {
+ /* If nothing was transferred, then return the -EAGAIN
+ * error (not zero which means end of file).
+ */
+
+ if (recvd < 1)
+ {
+ recvd = -EAGAIN;
+ }
+ break;
+ }
+#else
+ /* No... the circular buffer is empty. Have we returned anything
+ * to the caller?
+ */
+
+ else if (recvd > 0)
+ {
+ /* Yes.. break out of the loop and return the number of bytes
+ * received up to the wait condition.
+ */
+
+ break;
+ }
+
+ /* No... then we would have to wait to get receive some data.
+ * If the user has specified the O_NONBLOCK option, then do not
+ * wait.
+ */
+
+ else if (filep->f_oflags & O_NONBLOCK)
+ {
+ /* Break out of the loop returning -EAGAIN */
+
+ recvd = -EAGAIN;
+ break;
+ }
+#endif
+ /* Otherwise we are going to have to wait for data to arrive */
+
+ else
+ {
+ /* Disable Rx interrupts and test again... */
+
+ uart_disablerxint(dev);
+
+ /* If the Rx ring buffer still empty? Bytes may have been addded
+ * between the last time that we checked and when we disabled Rx
+ * interrupts.
+ */
+
+ if (dev->recv.head == dev->recv.tail)
+ {
+ /* Yes.. the buffer is still empty. Wait for some characters
+ * to be received into the buffer with the RX interrupt re-
+ * enabled. All interrupts are disabled briefly to assure
+ * that the following operations are atomic.
+ */
+
+ flags = irqsave();
+ dev->recvwaiting = true;
+ uart_enablerxint(dev);
+
+ /* Now wait with the Rx interrupt re-enabled. NuttX will
+ * automatically re-enable global interrupts when this thread
+ * goes to sleep.
+ */
+
+ ret = uart_takesem(&dev->recvsem, true);
+ irqrestore(flags);
+
+ /* Was a signal received while waiting for data to be received? */
+
+ if (ret < 0)
+ {
+ /* POSIX requires that we return after a signal is received.
+ * If some bytes were read, we need to return the number of bytes
+ * read; if no bytes were read, we need to return -1 with the
+ * errno set correctly.
+ */
+
+ if (recvd == 0)
+ {
+ /* No bytes were read, return -EINTR (the VFS layer will
+ * set the errno value appropriately.
+ */
+
+ recvd = -EINTR;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ /* No... the ring buffer is no longer empty. Just re-enable Rx
+ * interrupts and accept the new data on the next time through
+ * the loop.
+ */
+
+ uart_enablerxint(dev);
+ }
+ }
+ }
+
+ uart_givesem(&dev->recv.sem);
+ return recvd;
+}
+
+/************************************************************************************
+ * Name: uart_ioctl
+ ************************************************************************************/
+
+static int uart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR uart_dev_t *dev = inode->i_private;
+
+ return dev->ops->ioctl(filep, cmd, arg);
+}
+
+/****************************************************************************
+ * Name: uart_poll
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+int uart_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR uart_dev_t *dev = inode->i_private;
+ pollevent_t eventset;
+ int ndx;
+ int ret;
+ int i;
+
+ /* Some sanity checking */
+
+#if CONFIG_DEBUG
+ if (!dev || !fds)
+ {
+ return -ENODEV;
+ }
+#endif
+
+ /* Are we setting up the poll? Or tearing it down? */
+
+ ret = uart_takesem(&dev->pollsem, true);
+ if (ret < 0)
+ {
+ /* A signal received while waiting for access to the poll data
+ * will abort the operation.
+ */
+
+ return ret;
+ }
+
+ if (setup)
+ {
+ /* This is a request to set up the poll. Find an available
+ * slot for the poll structure reference
+ */
+
+ for (i = 0; i < CONFIG_SERIAL_NPOLLWAITERS; i++)
+ {
+ /* Find an available slot */
+
+ if (!dev->fds[i])
+ {
+ /* Bind the poll structure and this slot */
+
+ dev->fds[i] = fds;
+ fds->priv = &dev->fds[i];
+ break;
+ }
+ }
+
+ if (i >= CONFIG_SERIAL_NPOLLWAITERS)
+ {
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto errout;
+ }
+
+ /* Should we immediately notify on any of the requested events?
+ * First, check if the xmit buffer is full.
+ *
+ * Get exclusive access to the xmit buffer indices. NOTE: that we do not
+ * let this wait be interrupted by a signal (we probably should, but that
+ * would be a little awkward).
+ */
+
+ eventset = 0;
+ (void)uart_takesem(&dev->xmit.sem, false);
+
+ ndx = dev->xmit.head + 1;
+ if (ndx >= dev->xmit.size)
+ {
+ ndx = 0;
+ }
+
+ if (ndx != dev->xmit.tail)
+ {
+ eventset |= POLLOUT;
+ }
+
+ uart_givesem(&dev->xmit.sem);
+
+ /* Check if the receive buffer is empty
+ *
+ * Get exclusive access to the recv buffer indices. NOTE: that we do not
+ * let this wait be interrupted by a signal (we probably should, but that
+ * would be a little awkward).
+ */
+
+ (void)uart_takesem(&dev->recv.sem, false);
+ if (dev->recv.head != dev->recv.tail)
+ {
+ eventset |= POLLIN;
+ }
+
+ uart_givesem(&dev->recv.sem);
+
+ if (eventset)
+ {
+ uart_pollnotify(dev, eventset);
+ }
+
+ }
+ else if (fds->priv)
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+
+#ifdef CONFIG_DEBUG
+ if (!slot)
+ {
+ ret = -EIO;
+ goto errout;
+ }
+#endif
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+errout:
+ uart_givesem(&dev->pollsem);
+ return ret;
+}
+#endif
+
+/************************************************************************************
+ * Name: uart_close
+ *
+ * Description:
+ * This routine is called when the serial port gets closed.
+ * It waits for the last remaining data to be sent.
+ *
+ ************************************************************************************/
+
+static int uart_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR uart_dev_t *dev = inode->i_private;
+ irqstate_t flags;
+
+ /* Get exclusive access to the close semaphore (to synchronize open/close operations.
+ * NOTE: that we do not let this wait be interrupted by a signal. Technically, we
+ * should, but almost no one every checks the return value from close() so we avoid
+ * a potential memory leak by ignoring signals in this case.
+ */
+
+ (void)uart_takesem(&dev->closesem, false);
+ if (dev->open_count > 1)
+ {
+ dev->open_count--;
+ uart_givesem(&dev->closesem);
+ return OK;
+ }
+
+ /* There are no more references to the port */
+
+ dev->open_count = 0;
+
+ /* Stop accepting input */
+
+ uart_disablerxint(dev);
+
+ /* Now we wait for the transmit buffer to clear */
+
+ while (dev->xmit.head != dev->xmit.tail)
+ {
+#ifndef CONFIG_DISABLE_SIGNALS
+ usleep(HALF_SECOND_USEC);
+#else
+ up_mdelay(HALF_SECOND_MSEC);
+#endif
+ }
+
+ /* And wait for the TX fifo to drain */
+
+ while (!uart_txempty(dev))
+ {
+#ifndef CONFIG_DISABLE_SIGNALS
+ usleep(HALF_SECOND_USEC);
+#else
+ up_mdelay(HALF_SECOND_MSEC);
+#endif
+ }
+
+ /* Free the IRQ and disable the UART */
+
+ flags = irqsave(); /* Disable interrupts */
+ uart_detach(dev); /* Detach interrupts */
+ if (!dev->isconsole) /* Check for the serial console UART */
+ {
+ uart_shutdown(dev); /* Disable the UART */
+ }
+ irqrestore(flags);
+
+ uart_givesem(&dev->closesem);
+ return OK;
+ }
+
+/************************************************************************************
+ * Name: uart_open
+ *
+ * Description:
+ * This routine is called whenever a serial port is opened.
+ *
+ ************************************************************************************/
+
+static int uart_open(FAR struct file *filep)
+{
+ struct inode *inode = filep->f_inode;
+ uart_dev_t *dev = inode->i_private;
+ uint8_t tmp;
+ int ret;
+
+ /* If the port is the middle of closing, wait until the close is finished.
+ * If a signal is received while we are waiting, then return EINTR.
+ */
+
+ ret = uart_takesem(&dev->closesem, true);
+ if (ret < 0)
+ {
+ /* A signal received while waiting for the last close operation. */
+
+ return ret;
+ }
+
+ /* Start up serial port */
+ /* Increment the count of references to the device. */
+
+ tmp = dev->open_count + 1;
+ if (tmp == 0)
+ {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ goto errout_with_sem;
+ }
+
+ /* Check if this is the first time that the driver has been opened. */
+
+ if (tmp == 1)
+ {
+ irqstate_t flags = irqsave();
+
+ /* If this is the console, then the UART has already been initialized. */
+
+ if (!dev->isconsole)
+ {
+ /* Perform one time hardware initialization */
+
+ ret = uart_setup(dev);
+ if (ret < 0)
+ {
+ irqrestore(flags);
+ goto errout_with_sem;
+ }
+ }
+
+ /* In any event, we do have to configure for interrupt driven mode of
+ * operation. Attach the hardware IRQ(s). Hmm.. should shutdown() the
+ * the device in the rare case that uart_attach() fails, tmp==1, and
+ * this is not the console.
+ */
+
+ ret = uart_attach(dev);
+ if (ret < 0)
+ {
+ uart_shutdown(dev);
+ irqrestore(flags);
+ goto errout_with_sem;
+ }
+
+ /* Mark the io buffers empty */
+
+ dev->xmit.head = 0;
+ dev->xmit.tail = 0;
+ dev->recv.head = 0;
+ dev->recv.tail = 0;
+
+ /* Enable the RX interrupt */
+
+ uart_enablerxint(dev);
+ irqrestore(flags);
+ }
+
+ /* Save the new open count on success */
+
+ dev->open_count = tmp;
+
+errout_with_sem:
+ uart_givesem(&dev->closesem);
+ return ret;
+}
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: uart_register
+ *
+ * Description:
+ * Register serial console and serial ports.
+ *
+ ************************************************************************************/
+
+int uart_register(FAR const char *path, FAR uart_dev_t *dev)
+{
+ sem_init(&dev->xmit.sem, 0, 1);
+ sem_init(&dev->recv.sem, 0, 1);
+ sem_init(&dev->closesem, 0, 1);
+ sem_init(&dev->xmitsem, 0, 0);
+ sem_init(&dev->recvsem, 0, 0);
+#ifndef CONFIG_DISABLE_POLL
+ sem_init(&dev->pollsem, 0, 1);
+#endif
+
+ dbg("Registering %s\n", path);
+ return register_driver(path, &g_serialops, 0666, dev);
+}
+
+/************************************************************************************
+ * Name: uart_datareceived
+ *
+ * Description:
+ * This function is called from uart_recvchars when new serial data is place in
+ * the driver's circular buffer. This function will wake-up any stalled read()
+ * operations that are waiting for incoming data.
+ *
+ ************************************************************************************/
+
+void uart_datareceived(FAR uart_dev_t *dev)
+{
+ /* Awaken any awaiting read() operations */
+
+ if (dev->recvwaiting)
+ {
+ dev->recvwaiting = false;
+ (void)sem_post(&dev->recvsem);
+ }
+
+ /* Notify all poll/select waiters that they can read from the recv buffer */
+
+ uart_pollnotify(dev, POLLIN);
+
+}
+
+/************************************************************************************
+ * Name: uart_datasent
+ *
+ * Description:
+ * This function is called from uart_xmitchars after serial data has been sent,
+ * freeing up some space in the driver's circular buffer. This function will
+ * wake-up any stalled write() operations that was waiting for space to buffer
+ * outgoing data.
+ *
+ ************************************************************************************/
+
+void uart_datasent(FAR uart_dev_t *dev)
+{
+ if (dev->xmitwaiting)
+ {
+ dev->xmitwaiting = false;
+ (void)sem_post(&dev->xmitsem);
+ }
+
+ /* Notify all poll/select waiters that they can write to xmit buffer */
+
+ uart_pollnotify(dev, POLLOUT);
+}
+
+
diff --git a/nuttx/drivers/serial/serialirq.c b/nuttx/drivers/serial/serialirq.c
new file mode 100644
index 000000000..fda5b4afb
--- /dev/null
+++ b/nuttx/drivers/serial/serialirq.c
@@ -0,0 +1,186 @@
+/************************************************************************************
+ * drivers/serial/serialirq.c
+ *
+ * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ************************************************************************************/
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <semaphore.h>
+#include <debug.h>
+#include <nuttx/serial/serial.h>
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Private Types
+ ************************************************************************************/
+
+/************************************************************************************
+ * Private Function Prototypes
+ ************************************************************************************/
+
+/************************************************************************************
+ * Private Variables
+ ************************************************************************************/
+
+/************************************************************************************
+ * Private Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: uart_xmitchars
+ *
+ * Description:
+ * This function is called from the UART interrupt handler when an interrupt
+ * is received indicating that there is more space in the transmit FIFO. This
+ * function will send characters from the tail of the xmit buffer while the driver
+ * write() logic adds data to the head of the xmit buffer.
+ *
+ ************************************************************************************/
+
+void uart_xmitchars(FAR uart_dev_t *dev)
+{
+ uint16_t nbytes = 0;
+
+ /* Send while we still have data & room in the fifo */
+
+ while (dev->xmit.head != dev->xmit.tail && uart_txready(dev))
+ {
+ /* Send the next byte */
+
+ uart_send(dev, dev->xmit.buffer[dev->xmit.tail]);
+ nbytes++;
+
+ /* Increment the tail index */
+
+ if (++(dev->xmit.tail) >= dev->xmit.size)
+ {
+ dev->xmit.tail = 0;
+ }
+ }
+
+ /* When all of the characters have been sent from the buffer disable the TX
+ * interrupt.
+ */
+
+ if (dev->xmit.head == dev->xmit.tail)
+ {
+ uart_disabletxint(dev);
+ }
+
+ /* If any bytes were removed from the buffer, inform any waiters there there is
+ * space available.
+ */
+
+ if (nbytes)
+ {
+ uart_datasent(dev);
+ }
+}
+
+/************************************************************************************
+ * Name: uart_receivechars
+ *
+ * Description:
+ * This function is called from the UART interrupt handler when an interrupt
+ * is received indicating that are bytes available in the receive fifo. This
+ * function will add chars to head of receive buffer. Driver read() logic will
+ * take characters from the tail of the buffer.
+ *
+ ************************************************************************************/
+
+void uart_recvchars(FAR uart_dev_t *dev)
+{
+ unsigned int status;
+ int nexthead = dev->recv.head + 1;
+ uint16_t nbytes = 0;
+
+ if (nexthead >= dev->recv.size)
+ {
+ nexthead = 0;
+ }
+
+ /* Loop putting characters into the receive buffer until either there are no
+ * further characters to available.
+ */
+
+ while (uart_rxavailable(dev))
+ {
+ char ch = uart_receive(dev, &status);
+
+ /* If the RX buffer becomes full, then the serial data is discarded. This is
+ * necessary because on most serial hardware, you must read the data in order
+ * to clear the RX interrupt. An option on some hardware might be to simply
+ * disable RX interrupts until the RX buffer becomes non-FULL. However, that
+ * would probably just cause the overrun to occur in hardware (unless it has
+ * some large internal buffering).
+ */
+
+ if (nexthead != dev->recv.tail)
+ {
+ /* Add the character to the buffer */
+
+ dev->recv.buffer[dev->recv.head] = ch;
+ nbytes++;
+
+ /* Increment the head index */
+
+ dev->recv.head = nexthead;
+ if (++nexthead >= dev->recv.size)
+ {
+ nexthead = 0;
+ }
+ }
+ }
+
+ /* If any bytes were added to the buffer, inform any waiters there there is new
+ * incoming data available.
+ */
+
+ if (nbytes)
+ {
+ uart_datareceived(dev);
+ }
+}
diff --git a/nuttx/drivers/serial/uart_16550.c b/nuttx/drivers/serial/uart_16550.c
new file mode 100644
index 000000000..8fb71bfd2
--- /dev/null
+++ b/nuttx/drivers/serial/uart_16550.c
@@ -0,0 +1,1164 @@
+/****************************************************************************
+ * drivers/serial/uart_16550.c
+ * Serial driver for 16550 UART
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/uart_16550.h>
+
+#include <arch/board/board.h>
+
+#ifdef CONFIG_16550_UART
+
+/****************************************************************************
+ * Pre-processor definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct u16550_s
+{
+ uart_addrwidth_t uartbase; /* Base address of UART registers */
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ uint32_t baud; /* Configured baud */
+ uint32_t uartclk; /* UART clock frequency */
+#endif
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ uart_datawidth_t ier; /* Saved IER value */
+ uint8_t irq; /* IRQ associated with this UART */
+#endif
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ uint8_t parity; /* 0=none, 1=odd, 2=even */
+ uint8_t bits; /* Number of bits (7 or 8) */
+ bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int u16550_setup(struct uart_dev_s *dev);
+static void u16550_shutdown(struct uart_dev_s *dev);
+static int u16550_attach(struct uart_dev_s *dev);
+static void u16550_detach(struct uart_dev_s *dev);
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+static int u16550_interrupt(int irq, void *context);
+#endif
+static int u16550_ioctl(struct file *filep, int cmd, unsigned long arg);
+static int u16550_receive(struct uart_dev_s *dev, uint32_t *status);
+static void u16550_rxint(struct uart_dev_s *dev, bool enable);
+static bool u16550_rxavailable(struct uart_dev_s *dev);
+static void u16550_send(struct uart_dev_s *dev, int ch);
+static void u16550_txint(struct uart_dev_s *dev, bool enable);
+static bool u16550_txready(struct uart_dev_s *dev);
+static bool u16550_txempty(struct uart_dev_s *dev);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+struct uart_ops_s g_uart_ops =
+{
+ .setup = u16550_setup,
+ .shutdown = u16550_shutdown,
+ .attach = u16550_attach,
+ .detach = u16550_detach,
+ .ioctl = u16550_ioctl,
+ .receive = u16550_receive,
+ .rxint = u16550_rxint,
+ .rxavailable = u16550_rxavailable,
+ .send = u16550_send,
+ .txint = u16550_txint,
+ .txready = u16550_txready,
+ .txempty = u16550_txempty,
+};
+
+/* I/O buffers */
+
+#ifdef CONFIG_16550_UART0
+static char g_uart0rxbuffer[CONFIG_16550_UART0_RXBUFSIZE];
+static char g_uart0txbuffer[CONFIG_16550_UART0_TXBUFSIZE];
+#endif
+#ifdef CONFIG_16550_UART1
+static char g_uart1rxbuffer[CONFIG_16550_UART1_RXBUFSIZE];
+static char g_uart1txbuffer[CONFIG_16550_UART1_TXBUFSIZE];
+#endif
+#ifdef CONFIG_16550_UART2
+static char g_uart2rxbuffer[CONFIG_16550_UART2_RXBUFSIZE];
+static char g_uart2txbuffer[CONFIG_16550_UART2_TXBUFSIZE];
+#endif
+#ifdef CONFIG_16550_UART3
+static char g_uart3rxbuffer[CONFIG_16550_UART3_RXBUFSIZE];
+static char g_uart3txbuffer[CONFIG_16550_UART3_TXBUFSIZE];
+#endif
+
+/* This describes the state of the LPC17xx uart0 port. */
+
+#ifdef CONFIG_16550_UART0
+static struct u16550_s g_uart0priv =
+{
+ .uartbase = CONFIG_16550_UART0_BASE,
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ .baud = CONFIG_16550_UART0_BAUD,
+ .uartclk = CONFIG_16550_UART0_CLOCK,
+#endif
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ .irq = CONFIG_16550_UART0_IRQ,
+#endif
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ .parity = CONFIG_16550_UART0_PARITY,
+ .bits = CONFIG_16550_UART0_BITS,
+ .stopbits2 = CONFIG_16550_UART0_2STOP,
+#endif
+};
+
+static uart_dev_t g_uart0port =
+{
+ .recv =
+ {
+ .size = CONFIG_16550_UART0_RXBUFSIZE,
+ .buffer = g_uart0rxbuffer,
+ },
+ .xmit =
+ {
+ .size = CONFIG_16550_UART0_TXBUFSIZE,
+ .buffer = g_uart0txbuffer,
+ },
+ .ops = &g_uart_ops,
+ .priv = &g_uart0priv,
+};
+#endif
+
+/* This describes the state of the LPC17xx uart1 port. */
+
+#ifdef CONFIG_16550_UART1
+static struct u16550_s g_uart1priv =
+{
+ .uartbase = CONFIG_16550_UART1_BASE,
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ .baud = CONFIG_16550_UART1_BAUD,
+ .uartclk = CONFIG_16550_UART1_CLOCK,
+#endif
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ .irq = CONFIG_16550_UART1_IRQ,
+#endif
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ .parity = CONFIG_16550_UART1_PARITY,
+ .bits = CONFIG_16550_UART1_BITS,
+ .stopbits2 = CONFIG_16550_UART1_2STOP,
+#endif
+};
+
+static uart_dev_t g_uart1port =
+{
+ .recv =
+ {
+ .size = CONFIG_16550_UART1_RXBUFSIZE,
+ .buffer = g_uart1rxbuffer,
+ },
+ .xmit =
+ {
+ .size = CONFIG_16550_UART1_TXBUFSIZE,
+ .buffer = g_uart1txbuffer,
+ },
+ .ops = &g_uart_ops,
+ .priv = &g_uart1priv,
+};
+#endif
+
+/* This describes the state of the LPC17xx uart1 port. */
+
+#ifdef CONFIG_16550_UART2
+static struct u16550_s g_uart2priv =
+{
+ .uartbase = CONFIG_16550_UART2_BASE,
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ .baud = CONFIG_16550_UART2_BAUD,
+ .uartclk = CONFIG_16550_UART2_CLOCK,
+#endif
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ .irq = CONFIG_16550_UART2_IRQ,
+#endif
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ .parity = CONFIG_16550_UART2_PARITY,
+ .bits = CONFIG_16550_UART2_BITS,
+ .stopbits2 = CONFIG_16550_UART2_2STOP,
+#endif
+};
+
+static uart_dev_t g_uart2port =
+{
+ .recv =
+ {
+ .size = CONFIG_16550_UART2_RXBUFSIZE,
+ .buffer = g_uart2rxbuffer,
+ },
+ .xmit =
+ {
+ .size = CONFIG_16550_UART2_TXBUFSIZE,
+ .buffer = g_uart2txbuffer,
+ },
+ .ops = &g_uart_ops,
+ .priv = &g_uart2priv,
+};
+#endif
+
+/* This describes the state of the LPC17xx uart1 port. */
+
+#ifdef CONFIG_16550_UART3
+static struct u16550_s g_uart3priv =
+{
+ .uartbase = CONFIG_16550_UART3_BASE,
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ .baud = CONFIG_16550_UART3_BAUD,
+ .uartclk = CONFIG_16550_UART3_CLOCK,
+#endif
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ .irq = CONFIG_16550_UART3_IRQ,
+#endif
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ .parity = CONFIG_16550_UART3_PARITY,
+ .bits = CONFIG_16550_UART3_BITS,
+ .stopbits2 = CONFIG_16550_UART3_2STOP,
+#endif
+};
+
+static uart_dev_t g_uart3port =
+{
+ .recv =
+ {
+ .size = CONFIG_16550_UART3_RXBUFSIZE,
+ .buffer = g_uart3rxbuffer,
+ },
+ .xmit =
+ {
+ .size = CONFIG_16550_UART3_TXBUFSIZE,
+ .buffer = g_uart3txbuffer,
+ },
+ .ops = &g_uart_ops,
+ .priv = &g_uart3priv,
+};
+#endif
+
+/* Which UART with be tty0/console and which tty1? tty2? tty3? */
+
+#if defined(CONFIG_16550_UART0_SERIAL_CONSOLE)
+# define CONSOLE_DEV g_uart0port /* UART0=console */
+# define TTYS0_DEV g_uart0port /* UART0=ttyS0 */
+# ifdef CONFIG_16550_UART1
+# define TTYS1_DEV g_uart1port /* UART0=ttyS0;UART1=ttyS1 */
+# ifdef CONFIG_16550_UART2
+# define TTYS2_DEV g_uart2port /* UART0=ttyS0;UART1=ttyS1;UART2=ttyS2 */
+# ifdef CONFIG_16550_UART3
+# define TTYS3_DEV g_uart3port /* UART0=ttyS0;UART1=ttyS1;UART2=ttyS2;UART3=ttyS3 */
+# else
+# undef TTYS3_DEV /* UART0=ttyS0;UART1=ttyS1;UART2=ttyS;No ttyS3 */
+# endif
+# else
+# ifdef CONFIG_16550_UART3
+# define TTYS2_DEV g_uart3port /* UART0=ttyS0;UART1=ttyS1;UART3=ttys2;No ttyS3 */
+# else
+# undef TTYS2_DEV /* UART0=ttyS0;UART1=ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS3_DEV /* No ttyS3 */
+# endif
+# else
+# ifdef CONFIG_16550_UART2
+# define TTYS1_DEV g_uart2port /* UART0=ttyS0;UART2=ttyS1;No ttyS3 */
+# ifdef CONFIG_16550_UART3
+# define TTYS2_DEV g_uart3port /* UART0=ttyS0;UART2=ttyS1;UART3=ttyS2;No ttyS3 */
+# else
+# undef TTYS2_DEV /* UART0=ttyS0;UART2=ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS3_DEV /* No ttyS3 */
+# else
+# ifdef CONFIG_16550_UART3
+# define TTYS1_DEV g_uart3port /* UART0=ttyS0;UART3=ttyS1;No ttyS2;No ttyS3 */
+# else
+# undef TTYS1_DEV /* UART0=ttyS0;No ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS2_DEV /* No ttyS2 */
+# undef TTYS3_DEV /* No ttyS3 */
+# endif
+# endif
+#elif defined(CONFIG_16550_UART1_SERIAL_CONSOLE)
+# define CONSOLE_DEV g_uart1port /* UART1=console */
+# define TTYS0_DEV g_uart1port /* UART1=ttyS0 */
+# ifdef CONFIG_16550_UART
+# define TTYS1_DEV g_uart0port /* UART1=ttyS0;UART0=ttyS1 */
+# ifdef CONFIG_16550_UART2
+# define TTYS2_DEV g_uart2port /* UART1=ttyS0;UART0=ttyS1;UART2=ttyS2 */
+# ifdef CONFIG_16550_UART3
+# define TTYS3_DEV g_uart3port /* UART1=ttyS0;UART0=ttyS1;UART2=ttyS2;UART3=ttyS3 */
+# else
+# undef TTYS3_DEV /* UART1=ttyS0;UART0=ttyS1;UART2=ttyS;No ttyS3 */
+# endif
+# else
+# ifdef CONFIG_16550_UART3
+# define TTYS2_DEV g_uart3port /* UART1=ttyS0;UART0=ttyS1;UART3=ttys2;No ttyS3 */
+# else
+# undef TTYS2_DEV /* UART1=ttyS0;UART0=ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS3_DEV /* No ttyS3 */
+# endif
+# else
+# ifdef CONFIG_16550_UART2
+# define TTYS1_DEV g_uart2port /* UART1=ttyS0;UART2=ttyS1 */
+# ifdef CONFIG_16550_UART3
+# define TTYS2_DEV g_uart3port /* UART1=ttyS0;UART2=ttyS1;UART3=ttyS2;No ttyS3 */
+# else
+# undef TTYS2_DEV /* UART1=ttyS0;UART2=ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS3_DEV /* No ttyS3 */
+# else
+# ifdef CONFIG_16550_UART3
+# define TTYS1_DEV g_uart3port /* UART1=ttyS0;UART3=ttyS1;No ttyS2;No ttyS3 */
+# else
+# undef TTYS1_DEV /* UART1=ttyS0;No ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS2_DEV /* No ttyS2 */
+# undef TTYS3_DEV /* No ttyS3 */
+# endif
+# endif
+#elif defined(CONFIG_16550_UART2_SERIAL_CONSOLE)
+# define CONSOLE_DEV g_uart2port /* UART2=console */
+# define TTYS0_DEV g_uart2port /* UART2=ttyS0 */
+# ifdef CONFIG_16550_UART
+# define TTYS1_DEV g_uart0port /* UART2=ttyS0;UART0=ttyS1 */
+# ifdef CONFIG_16550_UART1
+# define TTYS2_DEV g_uart1port /* UART2=ttyS0;UART0=ttyS1;UART1=ttyS2 */
+# ifdef CONFIG_16550_UART3
+# define TTYS3_DEV g_uart3port /* UART2=ttyS0;UART0=ttyS1;UART1=ttyS2;UART3=ttyS3 */
+# else
+# undef TTYS3_DEV /* UART2=ttyS0;UART0=ttyS1;UART1=ttyS;No ttyS3 */
+# endif
+# else
+# ifdef CONFIG_16550_UART3
+# define TTYS2_DEV g_uart3port /* UART2=ttyS0;UART0=ttyS1;UART3=ttys2;No ttyS3 */
+# else
+# undef TTYS2_DEV /* UART2=ttyS0;UART0=ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS3_DEV /* No ttyS3 */
+# endif
+# else
+# ifdef CONFIG_16550_UART1
+# define TTYS1_DEV g_uart1port /* UART2=ttyS0;UART1=ttyS1 */
+# ifdef CONFIG_16550_UART3
+# define TTYS2_DEV g_uart3port /* UART2=ttyS0;UART1=ttyS1;UART3=ttyS2 */
+# else
+# undef TTYS2_DEV /* UART2=ttyS0;UART1=ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS3_DEV /* No ttyS3 */
+# else
+# ifdef CONFIG_16550_UART3
+# define TTYS1_DEV g_uart3port /* UART2=ttyS0;UART3=ttyS1;No ttyS3 */
+# else
+# undef TTYS1_DEV /* UART2=ttyS0;No ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS2_DEV /* No ttyS2 */
+# undef TTYS3_DEV /* No ttyS3 */
+# endif
+# endif
+#elif defined(CONFIG_16550_UART3_SERIAL_CONSOLE)
+# define CONSOLE_DEV g_uart3port /* UART3=console */
+# define TTYS0_DEV g_uart3port /* UART3=ttyS0 */
+# ifdef CONFIG_16550_UART
+# define TTYS1_DEV g_uart0port /* UART3=ttyS0;UART0=ttyS1 */
+# ifdef CONFIG_16550_UART1
+# define TTYS2_DEV g_uart1port /* UART3=ttyS0;UART0=ttyS1;UART1=ttyS2 */
+# ifdef CONFIG_16550_UART2
+# define TTYS3_DEV g_uart2port /* UART3=ttyS0;UART0=ttyS1;UART1=ttyS2;UART2=ttyS3 */
+# else
+# undef TTYS3_DEV /* UART3=ttyS0;UART0=ttyS1;UART1=ttyS;No ttyS3 */
+# endif
+# else
+# ifdef CONFIG_16550_UART2
+# define TTYS2_DEV g_uart2port /* UART3=ttyS0;UART0=ttyS1;UART2=ttys2;No ttyS3 */
+# else
+# undef TTYS2_DEV /* UART3=ttyS0;UART0=ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS3_DEV /* No ttyS3 */
+# endif
+# else
+# ifdef CONFIG_16550_UART1
+# define TTYS1_DEV g_uart1port /* UART3=ttyS0;UART1=ttyS1 */
+# ifdef CONFIG_16550_UART2
+# define TTYS2_DEV g_uart2port /* UART3=ttyS0;UART1=ttyS1;UART2=ttyS2;No ttyS3 */
+# else
+# undef TTYS2_DEV /* UART3=ttyS0;UART1=ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS3_DEV /* No ttyS3 */
+# else
+# ifdef CONFIG_16550_UART2
+# define TTYS1_DEV g_uart2port /* UART3=ttyS0;UART2=ttyS1;No ttyS3;No ttyS3 */
+# undef TTYS3_DEV /* UART3=ttyS0;UART2=ttyS1;No ttyS2;No ttyS3 */
+# else
+# undef TTYS1_DEV /* UART3=ttyS0;No ttyS1;No ttyS2;No ttyS3 */
+# endif
+# undef TTYS2_DEV /* No ttyS2 */
+# undef TTYS3_DEV /* No ttyS3 */
+# endif
+# endif
+#endif
+
+/************************************************************************************
+ * Inline Functions
+ ************************************************************************************/
+
+/****************************************************************************
+ * Name: u16550_serialin
+ ****************************************************************************/
+
+static inline uart_datawidth_t u16550_serialin(struct u16550_s *priv, int offset)
+{
+ return uart_getreg(priv->uartbase, offset);
+}
+
+/****************************************************************************
+ * Name: u16550_serialout
+ ****************************************************************************/
+
+static inline void u16550_serialout(struct u16550_s *priv, int offset, uart_datawidth_t value)
+{
+ uart_putreg(priv->uartbase, offset, value);
+}
+
+/****************************************************************************
+ * Name: u16550_disableuartint
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+static inline void u16550_disableuartint(struct u16550_s *priv, uart_datawidth_t *ier)
+{
+ if (ier)
+ {
+ *ier = priv->ier & UART_IER_ALLIE;
+ }
+
+ priv->ier &= ~UART_IER_ALLIE;
+ u16550_serialout(priv, UART_IER_OFFSET, priv->ier);
+}
+#else
+# define u16550_disableuartint(priv,ier)
+#endif
+
+/****************************************************************************
+ * Name: u16550_restoreuartint
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+static inline void u16550_restoreuartint(struct u16550_s *priv, uint32_t ier)
+{
+ priv->ier |= ier & UART_IER_ALLIE;
+ u16550_serialout(priv, UART_IER_OFFSET, priv->ier);
+}
+#else
+# define u16550_restoreuartint(priv,ier)
+#endif
+
+/****************************************************************************
+ * Name: u16550_enablebreaks
+ ****************************************************************************/
+
+static inline void u16550_enablebreaks(struct u16550_s *priv, bool enable)
+{
+ uint32_t lcr = u16550_serialin(priv, UART_LCR_OFFSET);
+ if (enable)
+ {
+ lcr |= UART_LCR_BRK;
+ }
+ else
+ {
+ lcr &= ~UART_LCR_BRK;
+ }
+ u16550_serialout(priv, UART_LCR_OFFSET, lcr);
+}
+
+/************************************************************************************
+ * Name: u16550_divisor
+ *
+ * Descrption:
+ * Select a divider to produce the BAUD from the UART_CLK.
+ *
+ * BAUD = UART_CLK / (16 * DL), or
+ * DIV = UART_CLK / BAUD / 16
+ *
+ * Ignoring the fractional divider for now.
+ *
+ ************************************************************************************/
+
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+static inline uint32_t u16550_divisor(struct u16550_s *priv)
+{
+ return (priv->uartclk + (priv->baud << 3)) / (priv->baud << 4);
+}
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: u16550_setup
+ *
+ * Description:
+ * Configure the UART baud, bits, parity, fifos, etc. This
+ * method is called the first time that the serial port is
+ * opened.
+ *
+ ****************************************************************************/
+
+static int u16550_setup(struct uart_dev_s *dev)
+{
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ uint16_t div;
+ uint32_t lcr;
+
+ /* Clear fifos */
+
+ u16550_serialout(priv, UART_FCR_OFFSET, (UART_FCR_RXRST|UART_FCR_TXRST));
+
+ /* Set trigger */
+
+ u16550_serialout(priv, UART_FCR_OFFSET, (UART_FCR_FIFOEN|UART_FCR_RXTRIGGER_8));
+
+ /* Set up the IER */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ priv->ier = u16550_serialin(priv, UART_IER_OFFSET);
+#endif
+
+ /* Set up the LCR */
+
+ lcr = 0;
+ switch (priv->bits)
+ {
+ case 5 :
+ lcr |= UART_LCR_WLS_7BIT;
+ break;
+
+ case 6 :
+ lcr |= UART_LCR_WLS_7BIT;
+ break;
+
+ case 7 :
+ lcr |= UART_LCR_WLS_7BIT;
+ break;
+
+ default:
+ case 8 :
+ lcr |= UART_LCR_WLS_7BIT;
+ break;
+ }
+
+ if (priv->stopbits2)
+ {
+ lcr |= UART_LCR_STB;
+ }
+
+ if (priv->parity == 1)
+ {
+ lcr |= UART_LCR_PEN;
+ }
+ else if (priv->parity == 2)
+ {
+ lcr |= (UART_LCR_PEN|UART_LCR_EPS);
+ }
+
+ /* Enter DLAB=1 */
+
+ u16550_serialout(priv, UART_LCR_OFFSET, (lcr | UART_LCR_DLAB));
+
+ /* Set the BAUD divisor */
+
+ div = u16550_divisor(priv);
+ u16550_serialout(priv, UART_DLM_OFFSET, div >> 8);
+ u16550_serialout(priv, UART_DLL_OFFSET, div & 0xff);
+
+ /* Clear DLAB */
+
+ u16550_serialout(priv, UART_LCR_OFFSET, lcr);
+
+ /* Configure the FIFOs */
+
+ u16550_serialout(priv, UART_FCR_OFFSET,
+ (UART_FCR_RXTRIGGER_8|UART_FCR_TXRST|UART_FCR_RXRST|UART_FCR_FIFOEN));
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Name: u16550_shutdown
+ *
+ * Description:
+ * Disable the UART. This method is called when the serial
+ * port is closed
+ *
+ ****************************************************************************/
+
+static void u16550_shutdown(struct uart_dev_s *dev)
+{
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ u16550_disableuartint(priv, NULL);
+}
+
+/****************************************************************************
+ * Name: u16550_attach
+ *
+ * Description:
+ * Configure the UART to operation in interrupt driven mode. This method is
+ * called when the serial port is opened. Normally, this is just after the
+ * the setup() method is called, however, the serial console may operate in
+ * a non-interrupt driven mode during the boot phase.
+ *
+ * RX and TX interrupts are not enabled when by the attach method (unless the
+ * hardware supports multiple levels of interrupt enabling). The RX and TX
+ * interrupts are not enabled until the txint() and rxint() methods are called.
+ *
+ ****************************************************************************/
+
+static int u16550_attach(struct uart_dev_s *dev)
+{
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ int ret;
+
+ /* Attach and enable the IRQ */
+
+ ret = irq_attach(priv->irq, u16550_interrupt);
+#ifndef CONFIG_ARCH_NOINTC
+ if (ret == OK)
+ {
+ /* Enable the interrupt (RX and TX interrupts are still disabled
+ * in the UART
+ */
+
+ up_enable_irq(priv->irq);
+ }
+#endif
+ return ret;
+#else
+ return OK;
+#endif
+}
+
+/****************************************************************************
+ * Name: u16550_detach
+ *
+ * Description:
+ * Detach UART interrupts. This method is called when the serial port is
+ * closed normally just before the shutdown method is called. The exception is
+ * the serial console which is never shutdown.
+ *
+ ****************************************************************************/
+
+static void u16550_detach(struct uart_dev_s *dev)
+{
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+#ifndef CONFIG_ARCH_NOINTC
+ up_disable_irq(priv->irq);
+#endif
+ irq_detach(priv->irq);
+#endif
+}
+
+/****************************************************************************
+ * Name: u16550_interrupt
+ *
+ * Description:
+ * This is the UART interrupt handler. It will be invoked when an
+ * interrupt received on the 'irq' It should call uart_transmitchars or
+ * uart_receivechar to perform the appropriate data transfers. The
+ * interrupt handling logic must be able to map the 'irq' number into the
+ * appropriate u16550_s structure in order to call these functions.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+static int u16550_interrupt(int irq, void *context)
+{
+ struct uart_dev_s *dev = NULL;
+ struct u16550_s *priv;
+ uint32_t status;
+ int passes;
+
+#ifdef CONFIG_16550_UART0
+ if (g_uart0priv.irq == irq)
+ {
+ dev = &g_uart0port;
+ }
+ else
+#endif
+#ifdef CONFIG_16550_UART1
+ if (g_uart1priv.irq == irq)
+ {
+ dev = &g_uart1port;
+ }
+ else
+#endif
+#ifdef CONFIG_16550_UART2
+ if (g_uart2priv.irq == irq)
+ {
+ dev = &g_uart2port;
+ }
+ else
+#endif
+#ifdef CONFIG_16550_UART3
+ if (g_uart3priv.irq == irq)
+ {
+ dev = &g_uart3port;
+ }
+#endif
+ ASSERT(dev != NULL);
+ priv = (struct u16550_s*)dev->priv;
+
+ /* Loop until there are no characters to be transferred or,
+ * until we have been looping for a long time.
+ */
+
+ for (passes = 0; passes < 256; passes++)
+ {
+ /* Get the current UART status and check for loop
+ * termination conditions
+ */
+
+ status = u16550_serialin(priv, UART_IIR_OFFSET);
+
+ /* The UART_IIR_INTSTATUS bit should be zero if there are pending
+ * interrupts
+ */
+
+ if ((status & UART_IIR_INTSTATUS) != 0)
+ {
+ /* Break out of the loop when there is no longer a
+ * pending interrupt
+ */
+
+ break;
+ }
+
+ /* Handle the interrupt by its interrupt ID field */
+
+ switch (status & UART_IIR_INTID_MASK)
+ {
+ /* Handle incoming, receive bytes (with or without timeout) */
+
+ case UART_IIR_INTID_RDA:
+ case UART_IIR_INTID_CTI:
+ {
+ uart_recvchars(dev);
+ break;
+ }
+
+ /* Handle outgoing, transmit bytes */
+
+ case UART_IIR_INTID_THRE:
+ {
+ uart_xmitchars(dev);
+ break;
+ }
+
+ /* Just clear modem status interrupts (UART1 only) */
+
+ case UART_IIR_INTID_MSI:
+ {
+ /* Read the modem status register (MSR) to clear */
+
+ status = u16550_serialin(priv, UART_MSR_OFFSET);
+ vdbg("MSR: %02x\n", status);
+ break;
+ }
+
+ /* Just clear any line status interrupts */
+
+ case UART_IIR_INTID_RLS:
+ {
+ /* Read the line status register (LSR) to clear */
+
+ status = u16550_serialin(priv, UART_LSR_OFFSET);
+ vdbg("LSR: %02x\n", status);
+ break;
+ }
+
+ /* There should be no other values */
+
+ default:
+ {
+ dbg("Unexpected IIR: %02x\n", status);
+ break;
+ }
+ }
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: u16550_ioctl
+ *
+ * Description:
+ * All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int u16550_ioctl(struct file *filep, int cmd, unsigned long arg)
+{
+ struct inode *inode = filep->f_inode;
+ struct uart_dev_s *dev = inode->i_private;
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ int ret = OK;
+
+ switch (cmd)
+ {
+ case TIOCSERGSTRUCT:
+ {
+ struct u16550_s *user = (struct u16550_s*)arg;
+ if (!user)
+ {
+ set_errno(EINVAL);
+ ret = ERROR;
+ }
+ else
+ {
+ memcpy(user, dev, sizeof(struct u16550_s));
+ }
+ }
+ break;
+
+ case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */
+ {
+ irqstate_t flags = irqsave();
+ u16550_enablebreaks(priv, true);
+ irqrestore(flags);
+ }
+ break;
+
+ case TIOCCBRK: /* BSD compatibility: Turn break off, unconditionally */
+ {
+ irqstate_t flags;
+ flags = irqsave();
+ u16550_enablebreaks(priv, false);
+ irqrestore(flags);
+ }
+ break;
+
+ default:
+ set_errno(ENOTTY);
+ ret = ERROR;
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: u16550_receive
+ *
+ * Description:
+ * Called (usually) from the interrupt level to receive one
+ * character from the UART. Error bits associated with the
+ * receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+static int u16550_receive(struct uart_dev_s *dev, uint32_t *status)
+{
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ uint32_t rbr;
+
+ *status = u16550_serialin(priv, UART_LSR_OFFSET);
+ rbr = u16550_serialin(priv, UART_RBR_OFFSET);
+ return rbr;
+}
+
+/****************************************************************************
+ * Name: u16550_rxint
+ *
+ * Description:
+ * Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+static void u16550_rxint(struct uart_dev_s *dev, bool enable)
+{
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ if (enable)
+ {
+ priv->ier |= UART_IER_ERBFI;
+ }
+ else
+ {
+ priv->ier &= ~UART_IER_ERBFI;
+ }
+ u16550_serialout(priv, UART_IER_OFFSET, priv->ier);
+#endif
+}
+
+/****************************************************************************
+ * Name: u16550_rxavailable
+ *
+ * Description:
+ * Return true if the receive fifo is not empty
+ *
+ ****************************************************************************/
+
+static bool u16550_rxavailable(struct uart_dev_s *dev)
+{
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ return ((u16550_serialin(priv, UART_LSR_OFFSET) & UART_LSR_DR) != 0);
+}
+
+/****************************************************************************
+ * Name: u16550_send
+ *
+ * Description:
+ * This method will send one byte on the UART
+ *
+ ****************************************************************************/
+
+static void u16550_send(struct uart_dev_s *dev, int ch)
+{
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ u16550_serialout(priv, UART_THR_OFFSET, (uart_datawidth_t)ch);
+}
+
+/****************************************************************************
+ * Name: u16550_txint
+ *
+ * Description:
+ * Call to enable or disable TX interrupts
+ *
+ ****************************************************************************/
+
+static void u16550_txint(struct uart_dev_s *dev, bool enable)
+{
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ irqstate_t flags;
+
+ flags = irqsave();
+ if (enable)
+ {
+ priv->ier |= UART_IER_ETBEI;
+ u16550_serialout(priv, UART_IER_OFFSET, priv->ier);
+
+ /* Fake a TX interrupt here by just calling uart_xmitchars() with
+ * interrupts disabled (note this may recurse).
+ */
+
+ uart_xmitchars(dev);
+ }
+ else
+ {
+ priv->ier &= ~UART_IER_ETBEI;
+ u16550_serialout(priv, UART_IER_OFFSET, priv->ier);
+ }
+
+ irqrestore(flags);
+#endif
+}
+
+/****************************************************************************
+ * Name: u16550_txready
+ *
+ * Description:
+ * Return true if the tranmsit fifo is not full
+ *
+ ****************************************************************************/
+
+static bool u16550_txready(struct uart_dev_s *dev)
+{
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ return ((u16550_serialin(priv, UART_LSR_OFFSET) & UART_LSR_THRE) != 0);
+}
+
+/****************************************************************************
+ * Name: u16550_txempty
+ *
+ * Description:
+ * Return true if the transmit fifo is empty
+ *
+ ****************************************************************************/
+
+static bool u16550_txempty(struct uart_dev_s *dev)
+{
+ struct u16550_s *priv = (struct u16550_s*)dev->priv;
+ return ((u16550_serialin(priv, UART_LSR_OFFSET) & UART_LSR_THRE) != 0);
+}
+
+/****************************************************************************
+ * Name: u16550_putc
+ *
+ * Description:
+ * Write one character to the UART (polled)
+ *
+ ****************************************************************************/
+
+static void u16550_putc(struct u16550_s *priv, int ch)
+{
+ while ((u16550_serialin(priv, UART_LSR_OFFSET) & UART_LSR_THRE) != 0);
+ u16550_serialout(priv, UART_THR_OFFSET, (uart_datawidth_t)ch);
+}
+
+/****************************************************************************
+ * Public Funtions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_earlyserialinit
+ *
+ * Description:
+ * Performs the low level UART initialization early in debug so that the
+ * serial console will be available during bootup. This must be called
+ * before uart_serialinit.
+ *
+ * NOTE: Configuration of the CONSOLE UART was performed by uart_lowsetup()
+ * very early in the boot sequence.
+ *
+ ****************************************************************************/
+
+void up_earlyserialinit(void)
+{
+ /* Configure all UARTs (except the CONSOLE UART) and disable interrupts */
+
+#ifdef CONFIG_16550_UART0
+ u16550_disableuartint(&g_uart0priv, NULL);
+#endif
+#ifdef CONFIG_16550_UART1
+ u16550_disableuartint(&g_uart1priv, NULL);
+#endif
+#ifdef CONFIG_16550_UART2
+ u16550_disableuartint(&g_uart2priv, NULL);
+#endif
+#ifdef CONFIG_16550_UART3
+ u16550_disableuartint(&g_uart3priv, NULL);
+#endif
+
+ /* Configuration whichever one is the console */
+
+#ifdef CONSOLE_DEV
+ CONSOLE_DEV.isconsole = true;
+ u16550_setup(&CONSOLE_DEV);
+#endif
+}
+
+/****************************************************************************
+ * Name: up_serialinit
+ *
+ * Description:
+ * Register serial console and serial ports. This assumes that
+ * up_earlyserialinit was called previously.
+ *
+ ****************************************************************************/
+
+void up_serialinit(void)
+{
+#ifdef CONSOLE_DEV
+ (void)uart_register("/dev/console", &CONSOLE_DEV);
+#endif
+#ifdef TTYS0_DEV
+ (void)uart_register("/dev/ttyS0", &TTYS0_DEV);
+#endif
+#ifdef TTYS1_DEV
+ (void)uart_register("/dev/ttyS1", &TTYS1_DEV);
+#endif
+#ifdef TTYS2_DEV
+ (void)uart_register("/dev/ttyS2", &TTYS2_DEV);
+#endif
+#ifdef TTYS3_DEV
+ (void)uart_register("/dev/ttyS3", &TTYS3_DEV);
+#endif
+}
+
+/****************************************************************************
+ * Name: up_putc
+ *
+ * Description:
+ * Provide priority, low-level access to support OS debug writes
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_16550_CONSOLE
+int up_putc(int ch)
+{
+ struct u16550_s *priv = (struct u16550_s*)CONSOLE_DEV.priv;
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ uart_datawidth_t ier;
+
+ u16550_disableuartint(priv, &ier);
+#endif
+
+ /* Check for LF */
+
+ if (ch == '\n')
+ {
+ /* Add CR */
+
+ u16550_putc(priv, '\r');
+ }
+
+ u16550_putc(priv, ch);
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+ u16550_restoreuartint(priv, ier);
+#endif
+ return ch;
+}
+#endif
+
+#endif /* CONFIG_16550_UART */
diff --git a/nuttx/drivers/syslog/Kconfig b/nuttx/drivers/syslog/Kconfig
new file mode 100644
index 000000000..3ec8c7490
--- /dev/null
+++ b/nuttx/drivers/syslog/Kconfig
@@ -0,0 +1,73 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+comment "System Logging"
+
+config RAMLOG
+ bool "RAM log device support"
+ default n
+ ---help---
+ This is a driver that was intended to support debugging output,
+ aka syslogging, when the normal serial output is not available.
+ For example, if you are using a telnet or USB serial console,
+ the debug output will get lost. However, the RAMLOG device should
+ be usable even if system logging is disabled.
+
+ This driver is similar to a pipe in that it saves the debugging
+ output in a FIFO in RAM. It differs from a pipe in numerous
+ details as needed to support logging.
+
+if RAMLOG
+config RAMLOG_SYSLOG
+ bool "Use RAMLOG for SYSLOG"
+ default n
+ depends on SYSLOG
+ ---help---
+ Use the RAM logging device for the syslogging interface. If this feature
+ is enabled (along with SYSLOG), then all debug output (only) will be re-directed
+ to the circular buffer in RAM. This RAM log can be view from NSH using the
+ 'dmesg'command.
+
+ Do not enable more than one SYSLOG device.
+
+config RAMLOG_CONSOLE
+ bool "Use RAMLOG for /dev/console"
+ default n
+ depends on DEV_CONSOLE
+ ---help---
+ Use the RAM logging device as a system console. If this feature is enabled (along
+ with DEV_CONSOLE), then all console output will be re-directed to a circular
+ buffer in RAM. This is useful, for example, if the only console is a Telnet
+ console. Then in that case, console output from non-Telnet threads will go to
+ the circular buffer and can be viewed using the NSH 'dmesg' command.
+
+config RAMLOG_CONSOLE_BUFSIZE
+ int "RAMLOG buffer size"
+ default 1024
+ depends on RAMLOG_SYSLOG || RAMLOG_CONSOLE
+ ---help---
+ Size of the console RAM log. Default: 1024
+
+config RAMLOG_CRLF
+ bool "RAMLOG CR/LF"
+ default n
+ ---help---
+ Pre-pend a carriage return before every linefeed that goes into the RAM log.
+
+config RAMLOG_NONBLOCKING
+ bool "RAMLOG non-block reads"
+ default y
+ ---help---
+ Reading from the RAMLOG will never block if the RAMLOG is empty. If the RAMLOG
+ is empty, then zero is returned (usually interpreted as end-of-file).
+
+config RAMLOG_NPOLLWAITERS
+ int "RAMLOG number of poll waiters"
+ default 4
+ depends on !DISABLE_POLL
+ ---help---
+ The maximum number of threads that may be waiting on the poll method.
+
+endif
diff --git a/nuttx/drivers/syslog/Make.defs b/nuttx/drivers/syslog/Make.defs
new file mode 100644
index 000000000..aa0ab19b8
--- /dev/null
+++ b/nuttx/drivers/syslog/Make.defs
@@ -0,0 +1,68 @@
+############################################################################
+# drivers/syslog/Make.defs
+# These drivers support system logging devices
+#
+# Copyright (C) 2012 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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 SYSLOG drivers (only one should be enabled)
+
+ifeq ($(CONFIG_SYSLOG),y)
+
+# If no special loggin devices are implemented, then the default SYSLOG
+# logic at fs/fs_syslog.c will be used
+
+# (Add other SYSLOG drivers here)
+
+ifeq ($(CONFIG_RAMLOG),y)
+ CSRCS += ramlog.c
+endif
+
+# Include SYSLOG build support
+
+DEPPATH += --dep-path syslog
+VPATH += :syslog
+
+else
+
+# The RAMLOG can be used even if system logging is not enabled.
+
+ifeq ($(CONFIG_RAMLOG),y)
+
+# Include RAMLOG build support
+
+CSRCS += ramlog.c
+DEPPATH += --dep-path syslog
+VPATH += :syslog
+
+endif
+endif
diff --git a/nuttx/drivers/syslog/README.txt b/nuttx/drivers/syslog/README.txt
new file mode 100644
index 000000000..bfef73ae8
--- /dev/null
+++ b/nuttx/drivers/syslog/README.txt
@@ -0,0 +1,64 @@
+drivers/syslog README File
+==========================
+
+This README file discusses the SYLOG drivers that can be found in the
+drivers/syslog directory. In NuttX, syslog output is equivalent to
+debug output and, therefore, the syslogging interfaces are defined in the
+header file include/debug.h.
+
+By default, all system log output goes to console (/dev/console). But that
+behavior can be changed by the defining CONFIG_SYSLOG in the NuttX
+configuration. In that, case all low-level debug output will go through
+syslog_putc().
+
+One version of syslog_putc() is defined in fs/fs_syslog.c; that version is
+used when CONFIG_SYSLOG_CHAR is defined. That version of syslog_putc()
+just integrates with the file system to re-direct debug output to a
+character device or to a file. A disadvantage of using the generic character
+device for the SYSLOG is that it cannot handle debug output generated from
+interrupt level handles.
+
+If CONFIG_SYSLOG_CHAR is not defined, then other custom SYSLOG drivers
+can be used. These custom SYSLOG drivers can do things like handle
+unusual logging media and since they can avoid the general file system
+interfaces, can be designed to support debug output from interrupt handlers.
+
+Those custom SYSLOG drivers reside in this directory.
+
+ramlog.c
+--------
+ The RAM logging driver is a driver that was intended to support debugging
+ output (syslogging) when the normal serial output is not available. For
+ example, if you are using a telnet or USB serial console, the debug
+ output will get lost.
+
+ The RAM logging driver is similar to a pipe in that it saves the
+ debugging output in a FIFO in RAM. It differs from a pipe in numerous
+ details as needed to support logging.
+
+ This driver is built when CONFIG_RAMLOG is defined in the Nuttx
+ configuration.
+
+ Configuration options:
+
+ CONFIG_RAMLOG - Enables the RAM logging feature
+ CONFIG_RAMLOG_CONSOLE - Use the RAM logging device as a system console.
+ If this feature is enabled (along with CONFIG_DEV_CONSOLE), then all
+ console output will be re-directed to a circular buffer in RAM. This
+ is useful, for example, if the only console is a Telnet console. Then
+ in that case, console output from non-Telnet threads will go to the
+ circular buffer and can be viewed using the NSH 'dmesg' command.
+ CONFIG_RAMLOG_SYSLOG - Use the RAM logging device for the syslogging
+ interface. If this feature is enabled (along with CONFIG_SYSLOG),
+ then all debug output (only) will be re-directed to the circular
+ buffer in RAM. This RAM log can be view from NSH using the 'dmesg'
+ command. NOTE: Unlike the limited, generic character driver SYSLOG
+ device, the RAMLOG *can* be used to generate debug output from interrupt
+ level handlers.
+ CONFIG_RAMLOG_NPOLLWAITERS - The number of threads than can be waiting
+ for this driver on poll(). Default: 4
+
+ If CONFIG_RAMLOG_CONSOLE or CONFIG_RAMLOG_SYSLOG is selected, then the
+ following may also be provided:
+
+ CONFIG_RAMLOG_CONSOLE_BUFSIZE - Size of the console RAM log. Default: 1024
diff --git a/nuttx/drivers/syslog/ramlog.c b/nuttx/drivers/syslog/ramlog.c
new file mode 100644
index 000000000..b3a2ad0f5
--- /dev/null
+++ b/nuttx/drivers/syslog/ramlog.c
@@ -0,0 +1,770 @@
+/****************************************************************************
+ * drivers/syslog/ramlog.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <poll.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/ramlog.h>
+
+#include <arch/irq.h>
+
+#ifdef CONFIG_RAMLOG
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ramlog_dev_s
+{
+#ifndef CONFIG_RAMLOG_NONBLOCKING
+ volatile uint8_t rl_nwaiters; /* Number of threads waiting for data */
+#endif
+ volatile uint16_t rl_head; /* The head index (where data is added) */
+ volatile uint16_t rl_tail; /* The tail index (where data is removed) */
+ sem_t rl_exclsem; /* Enforces mutually exclusive access */
+#ifndef CONFIG_RAMLOG_NONBLOCKING
+ sem_t rl_waitsem; /* Used to wait for data */
+#endif
+ size_t rl_bufsize; /* Size of the RAM buffer */
+ FAR char *rl_buffer; /* Circular RAM buffer */
+
+ /* The following is a list if poll structures of threads waiting for
+ * driver events. The 'struct pollfd' reference for each open is also
+ * retained in the f_priv field of the 'struct file'.
+ */
+
+#ifndef CONFIG_DISABLE_POLL
+ struct pollfd *rl_fds[CONFIG_RAMLOG_NPOLLWAITERS];
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* Helper functions */
+
+#ifndef CONFIG_DISABLE_POLL
+static void ramlog_pollnotify(FAR struct ramlog_dev_s *priv,
+ pollevent_t eventset);
+#endif
+static ssize_t ramlog_addchar(FAR struct ramlog_dev_s *priv, char ch);
+
+/* Character driver methods */
+
+static ssize_t ramlog_read(FAR struct file *, FAR char *, size_t);
+static ssize_t ramlog_write(FAR struct file *, FAR const char *, size_t);
+#ifndef CONFIG_DISABLE_POLL
+static int ramlog_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_ramlogfops =
+{
+ 0, /* open */
+ 0, /* close */
+ ramlog_read, /* read */
+ ramlog_write, /* write */
+ 0, /* seek */
+ 0 /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , ramlog_poll /* poll */
+#endif
+};
+
+/* This is the pre-allocated buffer used for the console RAM log and/or
+ * for the syslogging function.
+ */
+
+#if defined(CONFIG_RAMLOG_CONSOLE) || defined(CONFIG_RAMLOG_SYSLOG)
+static char g_sysbuffer[CONFIG_RAMLOG_CONSOLE_BUFSIZE];
+
+/* This is the device structure for the console or syslogging function. It
+ * must be statically initialized because the RAMLOG syslog_putc function
+ * could be called before the driver initialization logic executes.
+ */
+
+static struct ramlog_dev_s g_sysdev =
+{
+#ifndef CONFIG_RAMLOG_NONBLOCKING
+ 0, /* rl_nwaiters */
+#endif
+ 0, /* rl_head */
+ 0, /* rl_tail */
+ SEM_INITIALIZER(1), /* rl_exclsem */
+#ifndef CONFIG_RAMLOG_NONBLOCKING
+ SEM_INITIALIZER(0), /* rl_waitsem */
+#endif
+ CONFIG_RAMLOG_CONSOLE_BUFSIZE, /* rl_bufsize */
+ g_sysbuffer /* rl_buffer */
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ramlog_pollnotify
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static void ramlog_pollnotify(FAR struct ramlog_dev_s *priv,
+ pollevent_t eventset)
+{
+ FAR struct pollfd *fds;
+ irqstate_t flags;
+ int i;
+
+ /* This function may be called from an interrupt handler */
+
+ for (i = 0; i < CONFIG_RAMLOG_NPOLLWAITERS; i++)
+ {
+ flags = irqsave();
+ fds = priv->rl_fds[i];
+ if (fds)
+ {
+ fds->revents |= (fds->events & eventset);
+ if (fds->revents != 0)
+ {
+ sem_post(fds->sem);
+ }
+ }
+ irqrestore(flags);
+ }
+}
+#else
+# define ramlog_pollnotify(priv,event)
+#endif
+
+/****************************************************************************
+ * Name: ramlog_addchar
+ ****************************************************************************/
+
+static int ramlog_addchar(FAR struct ramlog_dev_s *priv, char ch)
+{
+ irqstate_t flags;
+ int nexthead;
+
+ /* Disable interrupts (in case we are NOT called from interrupt handler) */
+
+ flags = irqsave();
+
+ /* Calculate the write index AFTER the next byte is written */
+
+ nexthead = priv->rl_head + 1;
+ if (nexthead >= priv->rl_bufsize)
+ {
+ nexthead = 0;
+ }
+
+ /* Would the next write overflow the circular buffer? */
+
+ if (nexthead == priv->rl_tail)
+ {
+ /* Yes... Return an indication that nothing was saved in the buffer. */
+
+ irqrestore(flags);
+ return -EBUSY;
+ }
+
+ /* No... copy the byte and re-enable interrupts */
+
+ priv->rl_buffer[priv->rl_head] = ch;
+ priv->rl_head = nexthead;
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: ramlog_read
+ ****************************************************************************/
+
+static ssize_t ramlog_read(FAR struct file *filep, FAR char *buffer, size_t len)
+{
+ struct inode *inode = filep->f_inode;
+ struct ramlog_dev_s *priv;
+ ssize_t nread;
+ char ch;
+ int ret;
+
+ /* Some sanity checking */
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = inode->i_private;
+
+ /* If the circular buffer is empty, then wait for something to be written
+ * to it. This function may NOT be called from an interrupt handler.
+ */
+
+ DEBUGASSERT(!up_interrupt_context());
+
+ /* Get exclusive access to the rl_tail index */
+
+ ret = sem_wait(&priv->rl_exclsem);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Loop until something is read */
+
+ for (nread = 0; nread < len; )
+ {
+ /* Get the next byte from the buffer */
+
+ if (priv->rl_head == priv->rl_tail)
+ {
+ /* The circular buffer is empty. */
+
+#ifdef CONFIG_RAMLOG_NONBLOCKING
+ /* Return what we have (with zero mean the end-of-file) */
+
+ break;
+#else
+ /* Did we read anything? */
+
+ if (nread > 0)
+ {
+ /* Yes.. re-enable interrupts and the break out to return what
+ * we have.
+ */
+
+ break;
+ }
+
+ /* If the driver was opened with O_NONBLOCK option, then don't wait.
+ * Re-enable interrupts and return EGAIN.
+ */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ nread = -EAGAIN;
+ break;
+ }
+
+ /* Otherwise, wait for something to be written to the circular
+ * buffer. Increment the number of waiters so that the ramlog_write()
+ * will not that it needs to post the semaphore to wake us up.
+ */
+
+ sched_lock();
+ priv->rl_nwaiters++;
+ sem_post(&priv->rl_exclsem);
+
+ /* We may now be pre-empted! But that should be okay because we
+ * have already incremented nwaiters. Pre-emptions is disabled
+ * but will be re-enabled while we are waiting.
+ */
+
+ ret = sem_wait(&priv->rl_waitsem);
+
+ /* Interrupts will be disabled when we return. So the decrementing
+ * rl_nwaiters here is safe.
+ */
+
+ priv->rl_nwaiters--;
+ sched_unlock();
+
+ /* Did we successfully get the rl_waitsem? */
+
+ if (ret >= 0)
+ {
+ /* Yes... then retake the mutual exclusion semaphore */
+
+ ret = sem_wait(&priv->rl_exclsem);
+ }
+
+ /* Was the semaphore wait successful? Did we successful re-take the
+ * mutual exclusion semaphore?
+ */
+
+ if (ret < 0)
+ {
+ /* No.. One of the two sem_wait's failed. */
+
+ int errval = errno;
+
+ /* Were we awakened by a signal? Did we read anything before
+ * we received the signal?
+ */
+
+ if (errval != EINTR || nread >= 0)
+ {
+ /* Yes.. return the error. */
+
+ nread = -errval;
+ }
+
+ /* Break out to return what we have. Note, we can't exactly
+ * "break" out because whichever error occurred, we do not hold
+ * the exclusion semaphore.
+ */
+
+ goto errout_without_sem;
+ }
+#endif /* CONFIG_RAMLOG_NONBLOCKING */
+ }
+ else
+ {
+ /* The circular buffer is not empty, get the next byte from the
+ * tail index.
+ */
+
+ ch = priv->rl_buffer[priv->rl_tail];
+
+ /* Increment the tail index and re-enable interrupts */
+
+ if (++priv->rl_tail >= priv->rl_bufsize)
+ {
+ priv->rl_tail = 0;
+ }
+
+ /* Add the character to the user buffer */
+
+ buffer[nread] = ch;
+ nread++;
+ }
+ }
+
+ /* Relinquish the mutual exclusion semaphore */
+
+ sem_post(&priv->rl_exclsem);
+
+ /* Notify all poll/select waiters that they can write to the FIFO */
+
+#ifndef CONFIG_RAMLOG_NONBLOCKING
+errout_without_sem:
+#endif
+
+#ifndef CONFIG_DISABLE_POLL
+ if (nread > 0)
+ {
+ ramlog_pollnotify(priv, POLLOUT);
+ }
+#endif
+
+ /* Return the number of characters actually read */
+
+ return nread;
+}
+
+/****************************************************************************
+ * Name: ramlog_write
+ ****************************************************************************/
+
+static ssize_t ramlog_write(FAR struct file *filep, FAR const char *buffer, size_t len)
+{
+ struct inode *inode = filep->f_inode;
+ struct ramlog_dev_s *priv;
+ ssize_t nwritten;
+ char ch;
+ int ret;
+
+ /* Some sanity checking */
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = inode->i_private;
+
+ /* Loop until all of the bytes have been written. This function may be
+ * called from an interrupt handler! Semaphores cannot be used!
+ *
+ * The write logic only needs to modify the rl_head index. Therefore,
+ * there is a difference in the way that rl_head and rl_tail are protected:
+ * rl_tail is protected with a semaphore; rl_tail is protected by disabling
+ * interrupts.
+ */
+
+ for (nwritten = 0; nwritten < len; nwritten++)
+ {
+ /* Get the next character to output */
+
+ ch = buffer[nwritten];
+
+ /* Ignore carriage returns */
+
+#ifdef CONFIG_RAMLOG_CRLF
+ if (ch == '\r')
+ {
+ continue;
+ }
+
+ /* Pre-pend a carriage before a linefeed */
+
+ if (ch == '\n')
+ {
+ ret = ramlog_addchar(priv, '\r');
+ if (ret < 0)
+ {
+ /* The buffer is full and nothing was saved. Break out of the
+ * loop to return the number of bytes written up to this point.
+ * The data to be written is dropped on the floor.
+ */
+
+ break;
+ }
+ }
+#endif
+
+ /* Then output the character */
+
+ ret = ramlog_addchar(priv,ch);
+ if (ret < 0)
+ {
+ /* The buffer is full and nothing was saved. Break out of the
+ * loop to return the number of bytes written up to this point.
+ * The data to be written is dropped on the floor.
+ */
+
+ break;
+ }
+ }
+
+ /* Was anything written? */
+
+#if !defined(CONFIG_RAMLOG_NONBLOCKING) || !defined(CONFIG_DISABLE_POLL)
+ if (nwritten > 0)
+ {
+ irqstate_t flags;
+#ifndef CONFIG_RAMLOG_NONBLOCKING
+ int i;
+#endif
+
+ /* Are there threads waiting for read data? */
+
+ flags = irqsave();
+#ifndef CONFIG_RAMLOG_NONBLOCKING
+ for (i = 0; i < priv->rl_nwaiters; i++)
+ {
+ /* Yes.. Notify all of the waiting readers that more data is available */
+
+ sem_post(&priv->rl_waitsem);
+ }
+#endif
+
+ /* Notify all poll/select waiters that they can write to the FIFO */
+
+ ramlog_pollnotify(priv, POLLIN);
+ irqrestore(flags);
+ }
+#endif
+
+ /* We always have to return the number of bytes requested and NOT the
+ * number of bytes that were actually written. Otherwise, callers
+ * will think that this is a short write and probably retry (causing
+ */
+
+ return len;
+}
+
+/****************************************************************************
+ * Name: ramlog_poll
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+int ramlog_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct ramlog_dev_s *priv;
+ pollevent_t eventset;
+ int ndx;
+ int ret;
+ int i;
+
+ /* Some sanity checking */
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = inode->i_private;
+
+ /* Get exclusive access to the poll structures */
+
+ ret = sem_wait(&priv->rl_exclsem);
+ if (ret < 0)
+ {
+ int errval = errno;
+ return -errval;
+ }
+
+ /* Are we setting up the poll? Or tearing it down? */
+
+ if (setup)
+ {
+ /* This is a request to set up the poll. Find an available
+ * slot for the poll structure reference
+ */
+
+ for (i = 0; i < CONFIG_RAMLOG_NPOLLWAITERS; i++)
+ {
+ /* Find an available slot */
+
+ if (!priv->rl_fds[i])
+ {
+ /* Bind the poll structure and this slot */
+
+ priv->rl_fds[i] = fds;
+ fds->priv = &priv->rl_fds[i];
+ break;
+ }
+ }
+
+ if (i >= CONFIG_RAMLOG_NPOLLWAITERS)
+ {
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto errout;
+ }
+
+ /* Should immediately notify on any of the requested events?
+ * First, check if the xmit buffer is full.
+ */
+
+ eventset = 0;
+
+ ndx = priv->rl_head + 1;
+ if (ndx >= priv->rl_bufsize)
+ {
+ ndx = 0;
+ }
+
+ if (ndx != priv->rl_tail)
+ {
+ eventset |= POLLOUT;
+ }
+
+ /* Check if the receive buffer is empty */
+
+ if (priv->rl_head != priv->rl_tail)
+ {
+ eventset |= POLLIN;
+ }
+
+ if (eventset)
+ {
+ ramlog_pollnotify(priv, eventset);
+ }
+
+ }
+ else if (fds->priv)
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+
+#ifdef CONFIG_DEBUG
+ if (!slot)
+ {
+ ret = -EIO;
+ goto errout;
+ }
+#endif
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+errout:
+ sem_post(&priv->rl_exclsem);
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ramlog_register
+ *
+ * Description:
+ * Create the RAM logging device and register it at the specified path.
+ * Mostly likely this path will be /dev/console
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_RAMLOG_CONSOLE) && !defined(CONFIG_RAMLOG_SYSLOG)
+int ramlog_register(FAR const char *devpath, FAR char *buffer, size_t buflen)
+{
+ FAR struct ramlog_dev_s *priv;
+ int ret = -ENOMEM;
+
+ /* Sanity checking */
+
+ DEBUGASSERT(devpath && buffer && buflen > 1);
+
+ /* Allocate a RAM logging device structure */
+
+ priv = (struct ramlog_dev_s *)kzalloc(sizeof(struct ramlog_dev_s));
+ if (priv)
+ {
+ /* Initialize the non-zero values in the RAM logging device structure */
+
+ sem_init(&priv->rl_exclsem, 0, 1);
+#ifndef CONFIG_RAMLOG_NONBLOCKING
+ sem_init(&priv->rl_waitsem, 0, 0);
+#endif
+ priv->rl_bufsize = buflen;
+ priv->rl_buffer = buffer;
+
+ /* Register the character driver */
+
+ ret = register_driver(devpath, &g_ramlogfops, 0666, priv);
+ if (ret < 0)
+ {
+ kfree(priv);
+ }
+ }
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: ramlog_consoleinit
+ *
+ * Description:
+ * Create the RAM logging device and register it at the specified path.
+ * Mostly likely this path will be /dev/console
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RAMLOG_CONSOLE
+int ramlog_consoleinit(void)
+{
+ FAR struct ramlog_dev_s *priv = &g_sysdev;
+ int ret;
+
+ /* Register the console character driver */
+
+ ret = register_driver("/dev/console", &g_ramlogfops, 0666, priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: ramlog_sysloginit
+ *
+ * Description:
+ * Create the RAM logging device and register it at the specified path.
+ * Mostly likely this path will be CONFIG_RAMLOG_SYSLOG
+ *
+ * If CONFIG_RAMLOG_CONSOLE is also defined, then this functionality is
+ * performed when ramlog_consoleinit() is called.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RAMLOG_SYSLOG
+int ramlog_sysloginit(void)
+{
+ /* Register the syslog character driver */
+
+ return register_driver(CONFIG_SYSLOG_DEVPATH, &g_ramlogfops, 0666, &g_sysdev);
+}
+#endif
+
+/****************************************************************************
+ * Name: syslog_putc
+ *
+ * Description:
+ * This is the low-level system logging interface. The debugging/syslogging
+ * interfaces are lib_rawprintf() and lib_lowprinf(). The difference is
+ * the lib_rawprintf() writes to fd=1 (stdout) and lib_lowprintf() uses
+ * a lower level interface that works from interrupt handlers. This
+ * function is a a low-level interface used to implement lib_lowprintf()
+ * when CONFIG_RAMLOG_SYSLOG=y and CONFIG_SYSLOG=y
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_RAMLOG_CONSOLE) || defined(CONFIG_RAMLOG_SYSLOG)
+int syslog_putc(int ch)
+{
+ FAR struct ramlog_dev_s *priv = &g_sysdev;
+ int ret;
+
+ /* Ignore carriage returns */
+
+#ifdef CONFIG_RAMLOG_CRLF
+ if (ch == '\r')
+ {
+ return ch;
+ }
+
+ /* Pre-pend a newline with a carriage return */
+
+ if (ch == '\n')
+ {
+ ret = ramlog_addchar(priv, '\r');
+ if (ret < 0)
+ {
+ /* The buffer is full and nothing was saved. */
+
+ return ch;
+ }
+ }
+#endif
+
+ (void)ramlog_addchar(priv, ch);
+ return ch;
+}
+#endif
+
+#endif /* CONFIG_RAMLOG */
diff --git a/nuttx/drivers/usbdev/Kconfig b/nuttx/drivers/usbdev/Kconfig
new file mode 100644
index 000000000..70c7a04f0
--- /dev/null
+++ b/nuttx/drivers/usbdev/Kconfig
@@ -0,0 +1,495 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+menu "Device Controller Driver Options"
+
+config USBDEV_ISOCHRONOUS
+ bool "Enable isochronous"
+ default n
+ ---help---
+ Build in extra support for isochronous endpoints
+
+config USBDEV_DUALSPEED
+ bool "Enable high and full speed"
+ default n
+ ---help---
+ Hardware handles high and full speed operation (USB 2.0)
+
+choice USBDEV_POWERED
+ prompt "Select USB device powered"
+ default USBDEV_SELFPOWERED
+
+config USBDEV_SELFPOWERED
+ bool "Self powered"
+ ---help---
+ Will cause USB features to indicate that the device is self-powered
+
+config USBDEV_BUSPOWERED
+ bool "Bus powered"
+ ---help---
+ Will cause USB features to indicate that the device is self-powered
+
+endchoice
+
+config USBDEV_MAXPOWER
+ int "Maximum power consumption in mA"
+ default 100
+ depends on USBDEV_BUSPOWERED
+ ---help---
+ Maximum power consumption in mA
+
+config USBDEV_DMA
+ bool "Enable DMA methods"
+ default n
+ ---help---
+ Select this enable DMA-related methods in USB device controller driver
+ interface. These methods include the DMA buffer allocation methods:
+ allobuffer() and freebuffer().
+
+ The USB class driver allocates packet I/O buffers for data transfer by
+ calling the driver allocbuffer() and freebuffer() methods. Those methods
+ are only available if USBDEV_DMA is defined in the system configuration.
+
+config USBDEV_DMAMEMORY
+bool "Board DMA Allocation Hooks"
+ default n
+ depends on USBDEV_DMA
+ ---help---
+ The USB class driver allocates packet I/O buffers for data transfer by
+ calling the driver allocbuffer() and freebuffer() methods. Those methods
+ are only available if USBDEV_DMA is defined in the system configuration.
+
+ If USBDEV_DMAMEMORY is also defined in the NuttX configuration, then
+ the driver implementations of the allocbuffer() and freebuffer()
+ methods may use board-specific usbdev_dma_alloc() and usbdev_dma_free().
+ If USBDEV_DMA and USBDEV_DMAMEMORY are both defined, then the board-
+ specific logic must provide the functions usbdev_dma_alloc() and
+ usbdev_dma_free(): usbdev_dma_alloc() will allocate DMA-capable
+ memory of the specified size; usbdev_dma_free() is the corresponding
+ function that will be called to free the DMA-capable memory.
+
+config USBDEV_TRACE
+ bool "Enable USB tracing for debug"
+ default n
+ ---help---
+ Enables USB tracing for debug
+
+config USBDEV_TRACE_NRECORDS
+ int "Number of trace entries to remember"
+ default 32
+ depends on USBDEV_TRACE
+ ---help---
+ Number of trace entries to remember
+
+endmenu
+
+menuconfig USBDEV_COMPOSITE
+ bool "USB composite device support"
+ default n
+ ---help---
+ Enables USB composite device support
+
+if USBDEV_COMPOSITE
+
+#config COMPOSITE_IAD
+# bool ""
+# default n
+# ---help---
+# If one of the members of the composite has multiple interfaces
+# (such as CDC/ACM), then an Interface Association Descriptor (IAD)
+# will be necessary. Default: IAD will be used automatically if
+# needed. It should not be necessary to set this.
+
+config COMPOSITE_EP0MAXPACKET
+ int "Max packet size for endpoint 0"
+ default 64
+ ---help---
+ Max packet size for endpoint 0
+
+config COMPOSITE_VENDORID
+ hex "Composite vendor ID"
+ default 0
+
+config COMPOSITE_VENDORSTR
+ string "Composite vendor ID"
+ default "Nuttx"
+ ---help---
+ The vendor ID code/string
+
+config COMPOSITE_PRODUCTID
+ hex "Composite product id"
+ default 0
+
+config COMPOSITE_PRODUCTSTR
+ string "Composite product string"
+ default "Composite device"
+ ---help---
+ The product ID code/string
+
+config COMPOSITE_SERIALSTR
+ string "Composite serial string"
+ default "001"
+ ---help---
+ Device serial number string
+
+config COMPOSITE_CONFIGSTR
+ string "Configuration string"
+ default "Nuttx COMPOSITE config"
+ ---help---
+ Configuration string
+
+config COMPOSITE_VERSIONNO
+ string "Composite version number"
+ default ""
+ ---help---
+ Interface version number.
+endif
+
+menuconfig PL2303
+ bool "Emulates the Prolific PL2303 serial/USB converter"
+ default n
+ ---help---
+ This logic emulates the Prolific PL2303 serial/USB converter
+
+if PL2303
+config PL2303_EPINTIN
+ int "Logical endpoint numbers"
+ default 1
+
+config PL2303_EPBULKOUT
+ int "Endpoint Bulkout"
+ default 2
+
+config PL2303_EPBULKIN
+ int "Endpoint Bulkin"
+ default 3
+
+config PL2303_EP0MAXPACKET
+ int "Packet and request buffer sizes"
+ default 64
+
+config PL2303_NWRREQS
+ int "Number of read requests that can be in flight"
+ default 4
+ ---help---
+ The number of read requests that can be in flight
+
+config PL2303_NRDREQS
+ int "Number of write requests that can be in flight"
+ default 4
+ ---help---
+ The number of write/read requests that can be in flight
+
+config PL2303_RXBUFSIZE
+ int "Receive buffer size"
+ default 256
+ ---help---
+ Size of the serial receive/transmit buffers
+
+config PL2303_TXBUFSIZE
+ int "Transmit buffer size"
+ default 256
+ ---help---
+ Size of the serial receive/transmit buffers
+
+config PL2303_VENDORID
+ hex "Vendor ID"
+ default 0x067b
+
+config PL2303_PRODUCTID
+ hex "Product ID"
+ default 0x2303
+
+config PL2303_VENDORSTR
+ string "Vendor string"
+ default "NuttX"
+
+config PL2303_PRODUCTSTR
+ string "Product string"
+ default "USBdev Serial"
+endif
+
+menuconfig CDCACM
+ bool "USB Modem (CDC ACM) support"
+ default n
+ ---help---
+ Enables USB Modem (CDC ACM) support
+
+if CDCACM
+config CDCACM_COMPOSITE
+ bool "CDCACM composite support"
+ default n
+ depends on USBDEV_COMPOSITE
+ ---help---
+ Configure the CDC serial driver as part of a composite driver
+ (only if CONFIG_USBDEV_COMPOSITE is also defined)
+
+config CDCACM_IFNOBASE
+ int "Offset the CDC/ACM interface numbers"
+ default 0
+ depends on CDCACM_COMPOSITE
+ ---help---
+ If the CDC driver is part of a composite device, then this may need to
+ be defined to offset the CDC/ACM interface numbers so that they are
+ unique and contiguous. When used with the Mass Storage driver, the
+ correct value for this offset is zero.
+
+config CDCACM_STRBASE
+ int "Offset the CDC/ACM string numbers"
+ default 0
+ depends on CDCACM_COMPOSITE
+ ---help---
+ If the CDC driver is part of a composite device, then this may need to
+ be defined to offset the CDC/ACM string numbers so that they are
+ unique and contiguous. When used with the Mass Storage driver, the
+ correct value for this offset is four (this value actuallly only needs
+ to be defined if names are provided for the Notification interface,
+ config CDCACM_NOTIFSTR, or the data interface, CONFIG_CDCACM_DATAIFSTR).
+
+config CDCACM_EP0MAXPACKET
+ int "Endpoint 0 max packet size"
+ default 64
+ ---help---
+ Endpoint 0 max packet size. Default 64.
+
+config CDCACM_EPINTIN
+ int "Hardware endpoint that supports interrupt IN operation"
+ default 2
+ ---help---
+ The logical 7-bit address of a hardware endpoint that supports
+ interrupt IN operation. Default 2.
+
+config CDCACM_EPINTIN_FSSIZE
+ int "Endpoint in full speed size"
+ default 64
+ ---help---
+ Max package size for the interrupt IN endpoint if full speed mode.
+ Default 64.
+
+config CDCACM_EPINTIN_HSSIZE
+ int "Endpoint in high speed size"
+ default 64
+ ---help---
+ Max package size for the interrupt IN endpoint if high speed mode.
+ Default 64.
+
+config CDCACM_EPBULKOUT
+ int "Endpoint bulk out"
+ default 0
+ ---help---
+ The logical 7-bit address of a hardware endpoint that supports
+ bulk OUT operation
+
+config CDCACM_EPBULKOUT_FSSIZE
+ int "Endpoint bulk out full speed size"
+ default 64
+ ---help---
+ Max package size for the bulk OUT endpoint if full speed mode.
+ Default 64.
+
+config CDCACM_EPBULKOUT_HSSIZE
+ int "Endpoint bulk out high speed size"
+ default 512
+ ---help---
+ Max package size for the bulk OUT endpoint if high speed mode.
+ Default 512.
+
+config CDCACM_EPBULKIN
+ int "Endpoint bulk in"
+ default 0
+ ---help---
+ The logical 7-bit address of a hardware endpoint that supports
+ bulk IN operation
+
+config CDCACM_EPBULKIN_FSSIZE
+ int "Endpoint bulk in full speed size"
+ default 64
+ ---help---
+ Max package size for the bulk IN endpoint if full speed mode.
+ Default 64.
+
+config CDCACM_EPBULKIN_HSSIZE
+ int "Endpoint bulk in high speed size"
+ default 512
+ ---help---
+ Max package size for the bulk IN endpoint if high speed mode.
+ Default 512.
+
+config CDCACM_NWRREQS
+ int "Number of read requests that can be in flight"
+ default 4
+ ---help---
+ The number of read requests that can be in flight
+
+config CDCACM_NRDREQS
+ int "Number of write requests that can be in flight"
+ default 4
+ ---help---
+ The number of write/read requests that can be in flight
+
+config CDCACM_RXBUFSIZE
+ int "Receive buffer size"
+ default 256
+ ---help---
+ Size of the serial receive/transmit buffers
+
+config CDCACM_TXBUFSIZE
+ bool "Transmit buffer size"
+ default 256
+ ---help---
+ Size of the serial receive/transmit buffers
+
+config CDCACM_VENDORID
+ hex "Vendor ID"
+ default 0x0525
+ ---help---
+ The vendor ID code/string. Default 0x0525 and "NuttX"
+ 0x0525 is the Netchip vendor and should not be used in any
+ products. This default VID was selected for compatibility with
+ the Linux CDC ACM default VID.
+
+config CDCACM_PRODUCTID
+ hex "Product ID"
+ default 0xa4a7
+ ---help---
+ The product ID code/string. Default 0xa4a7 and "CDC/ACM Serial"
+ 0xa4a7 was selected for compatibility with the Linux CDC ACM
+ default PID.
+
+config CDCACM_VENDORSTR
+ string "Vendor string"
+ default "NuttX"
+
+config CDCACM_PRODUCTSTR
+ string "Product string"
+ default "USBdev Serial"
+endif
+
+menuconfig USBMSC
+ bool "USB Mass storage class device"
+ default n
+ ---help---
+ References:
+ "Universal Serial Bus Mass Storage Class, Specification Overview,"
+ Revision 1.2, USB Implementer's Forum, June 23, 2003.
+
+ "Universal Serial Bus Mass Storage Class, Bulk-Only Transport,"
+ Revision 1.0, USB Implementer's Forum, September 31, 1999.
+
+ "SCSI Primary Commands - 3 (SPC-3)," American National Standard
+ for Information Technology, May 4, 2005
+
+ "SCSI Primary Commands - 4 (SPC-4)," American National Standard
+ for Information Technology, July 19, 2008
+
+ "SCSI Block Commands -2 (SBC-2)," American National Standard
+ for Information Technology, November 13, 2004
+
+ "SCSI Multimedia Commands - 3 (MMC-3)," American National Standard
+ for Information Technology, November 12, 2001
+
+if USBMSC
+config USBMSC_COMPOSITE
+ bool "Mass storage composite support"
+ default n
+ depends on USBDEV_COMPOSITE
+ ---help---
+ Configure the mass storage driver as part of a composite driver
+ (only if CONFIG_USBDEV_COMPOSITE is also defined)
+
+config USBMSC_IFNOBASE
+ int "Offset the mass storage interface number"
+ default 2
+ depends on USBMSC_COMPOSITE
+ ---help---
+ If the CDC driver is part of a composite device, then this may need to
+ be defined to offset the mass storage interface number so that it is
+ unique and contiguous. When used with the CDC/ACM driver, the
+ correct value for this offset is two (because of the two CDC/ACM
+ interfaces that will precede it).
+
+config USBMSC_STRBASE
+ int "Offset the mass storage string numbers"
+ default 2
+ depends on USBMSC_COMPOSITE
+ ---help---
+ If the CDC driver is part of a composite device, then this may need to
+ be defined to offset the mass storage string numbers so that they are
+ unique and contiguous. When used with the CDC/ACM driver, the
+ correct value for this offset is four (or perhaps 5 or 6, depending
+ on if CONFIG_CDCACM_NOTIFSTR or CONFIG_CDCACM_DATAIFSTR are defined).
+
+config USBMSC_EP0MAXPACKET
+ int "Max packet size for endpoint 0"
+ default 64
+ ---help---
+ Max packet size for endpoint 0
+
+config USBMSC_EPBULKOUT
+ int "Endpoint bulk out"
+ default 0
+ ---help---
+ The logical 7-bit address of a hardware endpoints that support
+ bulk OUT and IN operations
+
+config USBMSC_EPBULKIN
+ int "Endpoint bulk in"
+ default 0
+ ---help---
+ The logical 7-bit address of a hardware endpoints that support
+ bulk OUT and IN operations
+
+config USBMSC_NWRREQS
+ int "The number of write requests that can be in flight"
+ default 4
+ ---help---
+ The number of write/read requests that can be in flight
+config USBMSC_NRDREQS
+ int "The number of read requests that can be in flight"
+ default 4
+ ---help---
+ The number of write/read requests that can be in flight
+
+config USBMSC_BULKINREQLEN
+ int "Bulk in request size"
+ default 512
+
+config USBMSC_BULKOUTREQLEN
+ int "Bulk out request size"
+ default 512
+ ---help---
+ The size of the buffer in each write/read request. This
+ value needs to be at least as large as the endpoint
+ maxpacket and ideally as large as a block device sector.
+
+config USBMSC_VENDORID
+ hex "Mass stroage Vendor ID"
+ default 0x00
+
+config USBMSC_VENDORSTR
+ string "Mass stroage vendor string"
+ default "Nuttx"
+ ---help---
+ The vendor ID code/string
+
+config USBMSC_PRODUCTID
+ hex "Mass stroage Product ID"
+ default 0x00
+
+config USBMSC_PRODUCTSTR
+ string "Mass stroage product string"
+ default "Mass stroage"
+
+config USBMSC_VERSIONNO
+ hex "USB MSC Version Number"
+ default "0x399"
+
+config USBMSC_REMOVABLE
+ bool "Mass stroage remove able"
+ default n
+ ---help---
+ Select if the media is removable
+ USB Composite Device Configuration
+endif
diff --git a/nuttx/drivers/usbdev/Make.defs b/nuttx/drivers/usbdev/Make.defs
new file mode 100644
index 000000000..f1b3c405a
--- /dev/null
+++ b/nuttx/drivers/usbdev/Make.defs
@@ -0,0 +1,63 @@
+############################################################################
+# drivers/usbdev/Make.defs
+#
+# Copyright (C) 2008, 2010-2012 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+ifeq ($(CONFIG_USBDEV),y)
+
+# Include USB device drivers
+
+ifeq ($(CONFIG_PL2303),y)
+ CSRCS += pl2303.c
+endif
+
+ifeq ($(CONFIG_CDCACM),y)
+ CSRCS += cdcacm.c cdcacm_desc.c
+endif
+
+ifeq ($(CONFIG_USBMSC),y)
+ CSRCS += usbmsc.c usbmsc_desc.c usbmsc_scsi.c
+endif
+
+ifeq ($(CONFIG_USBDEV_COMPOSITE),y)
+ CSRCS += composite.c composite_desc.c
+endif
+
+CSRCS += usbdev_trace.c usbdev_trprintf.c
+
+# Include USB device build support
+
+DEPPATH += --dep-path usbdev
+VPATH += :usbdev
+CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/usbdev}
+endif
diff --git a/nuttx/drivers/usbdev/cdcacm.c b/nuttx/drivers/usbdev/cdcacm.c
new file mode 100644
index 000000000..97c9d7c77
--- /dev/null
+++ b/nuttx/drivers/usbdev/cdcacm.c
@@ -0,0 +1,2212 @@
+/****************************************************************************
+ * drivers/usbdev/cdcacm.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <string.h>
+#include <errno.h>
+#include <queue.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/arch.h>
+#include <nuttx/serial/serial.h>
+
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/cdc.h>
+#include <nuttx/usb/usbdev.h>
+#include <nuttx/usb/cdcacm.h>
+#include <nuttx/usb/usbdev_trace.h>
+
+#include "cdcacm.h"
+
+#ifdef CONFIG_USBMSC_COMPOSITE
+# include <nuttx/usb/composite.h>
+# include "composite.h"
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Container to support a list of requests */
+
+struct cdcacm_req_s
+{
+ FAR struct cdcacm_req_s *flink; /* Implements a singly linked list */
+ FAR struct usbdev_req_s *req; /* The contained request */
+};
+
+/* This structure describes the internal state of the driver */
+
+struct cdcacm_dev_s
+{
+ FAR struct uart_dev_s serdev; /* Serial device structure */
+ FAR struct usbdev_s *usbdev; /* usbdev driver pointer */
+
+ uint8_t config; /* Configuration number */
+ uint8_t nwrq; /* Number of queue write requests (in reqlist)*/
+ uint8_t nrdq; /* Number of queue read requests (in epbulkout) */
+ uint8_t minor; /* The device minor number */
+ bool rxenabled; /* true: UART RX "interrupts" enabled */
+ int16_t rxhead; /* Working head; used when rx int disabled */
+
+ uint8_t ctrlline; /* Buffered control line state */
+ struct cdc_linecoding_s linecoding; /* Buffered line status */
+ cdcacm_callback_t callback; /* Serial event callback function */
+
+ FAR struct usbdev_ep_s *epintin; /* Interrupt IN endpoint structure */
+ FAR struct usbdev_ep_s *epbulkin; /* Bulk IN endpoint structure */
+ FAR struct usbdev_ep_s *epbulkout; /* Bulk OUT endpoint structure */
+ FAR struct usbdev_req_s *ctrlreq; /* Allocoated control request */
+ struct sq_queue_s reqlist; /* List of write request containers */
+
+ /* Pre-allocated write request containers. The write requests will
+ * be linked in a free list (reqlist), and used to send requests to
+ * EPBULKIN; Read requests will be queued in the EBULKOUT.
+ */
+
+ struct cdcacm_req_s wrreqs[CONFIG_CDCACM_NWRREQS];
+ struct cdcacm_req_s rdreqs[CONFIG_CDCACM_NWRREQS];
+
+ /* Serial I/O buffers */
+
+ char rxbuffer[CONFIG_CDCACM_RXBUFSIZE];
+ char txbuffer[CONFIG_CDCACM_TXBUFSIZE];
+};
+
+/* The internal version of the class driver */
+
+struct cdcacm_driver_s
+{
+ struct usbdevclass_driver_s drvr;
+ FAR struct cdcacm_dev_s *dev;
+};
+
+/* This is what is allocated */
+
+struct cdcacm_alloc_s
+{
+ struct cdcacm_dev_s dev;
+ struct cdcacm_driver_s drvr;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Transfer helpers *********************************************************/
+
+static uint16_t cdcacm_fillrequest(FAR struct cdcacm_dev_s *priv,
+ uint8_t *reqbuf, uint16_t reqlen);
+static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv);
+static inline int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
+ uint8_t *reqbuf, uint16_t reqlen);
+
+/* Request helpers *********************************************************/
+
+static struct usbdev_req_s *cdcacm_allocreq(FAR struct usbdev_ep_s *ep,
+ uint16_t len);
+static void cdcacm_freereq(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+
+/* Configuration ***********************************************************/
+
+static void cdcacm_resetconfig(FAR struct cdcacm_dev_s *priv);
+#ifdef CONFIG_USBDEV_DUALSPEED
+static int cdcacm_epconfigure(FAR struct usbdev_ep_s *ep,
+ enum cdcacm_epdesc_e epid, uint16_t mxpacket, bool last);
+#endif
+static int cdcacm_setconfig(FAR struct cdcacm_dev_s *priv,
+ uint8_t config);
+
+/* Completion event handlers ***********************************************/
+
+static void cdcacm_ep0incomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+static void cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+static void cdcacm_wrcomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+
+/* USB class device ********************************************************/
+
+static int cdcacm_bind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+static void cdcacm_unbind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+static int cdcacm_setup(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout,
+ size_t outlen);
+static void cdcacm_disconnect(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+
+/* UART Operations **********************************************************/
+
+static int cdcuart_setup(FAR struct uart_dev_s *dev);
+static void cdcuart_shutdown(FAR struct uart_dev_s *dev);
+static int cdcuart_attach(FAR struct uart_dev_s *dev);
+static void cdcuart_detach(FAR struct uart_dev_s *dev);
+static int cdcuart_ioctl(FAR struct file *filep,int cmd,unsigned long arg);
+static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable);
+static void cdcuart_txint(FAR struct uart_dev_s *dev, bool enable);
+static bool cdcuart_txempty(FAR struct uart_dev_s *dev);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+/* USB class device *********************************************************/
+
+static const struct usbdevclass_driverops_s g_driverops =
+{
+ cdcacm_bind, /* bind */
+ cdcacm_unbind, /* unbind */
+ cdcacm_setup, /* setup */
+ cdcacm_disconnect, /* disconnect */
+ NULL, /* suspend */
+ NULL, /* resume */
+};
+
+/* Serial port **************************************************************/
+
+static const struct uart_ops_s g_uartops =
+{
+ cdcuart_setup, /* setup */
+ cdcuart_shutdown, /* shutdown */
+ cdcuart_attach, /* attach */
+ cdcuart_detach, /* detach */
+ cdcuart_ioctl, /* ioctl */
+ NULL, /* receive */
+ cdcuart_rxint, /* rxinit */
+ NULL, /* rxavailable */
+ NULL, /* send */
+ cdcuart_txint, /* txinit */
+ NULL, /* txready */
+ cdcuart_txempty /* txempty */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cdcacm_fillrequest
+ *
+ * Description:
+ * If there is data to send it is copied to the given buffer. Called
+ * either to initiate the first write operation, or from the completion
+ * interrupt handler service consecutive write operations.
+ *
+ * NOTE: The USB serial driver does not use the serial drivers
+ * uart_xmitchars() API. That logic is essentially duplicated here because
+ * unlike UART hardware, we need to be able to handle writes not byte-by-byte,
+ * but packet-by-packet. Unfortunately, that decision also exposes some
+ * internals of the serial driver in the following.
+ *
+ ****************************************************************************/
+
+static uint16_t cdcacm_fillrequest(FAR struct cdcacm_dev_s *priv, uint8_t *reqbuf,
+ uint16_t reqlen)
+{
+ FAR uart_dev_t *serdev = &priv->serdev;
+ FAR struct uart_buffer_s *xmit = &serdev->xmit;
+ irqstate_t flags;
+ uint16_t nbytes = 0;
+
+ /* Disable interrupts */
+
+ flags = irqsave();
+
+ /* Transfer bytes while we have bytes available and there is room in the request */
+
+ while (xmit->head != xmit->tail && nbytes < reqlen)
+ {
+ *reqbuf++ = xmit->buffer[xmit->tail];
+ nbytes++;
+
+ /* Increment the tail pointer */
+
+ if (++(xmit->tail) >= xmit->size)
+ {
+ xmit->tail = 0;
+ }
+ }
+
+ /* When all of the characters have been sent from the buffer
+ * disable the "TX interrupt".
+ */
+
+ if (xmit->head == xmit->tail)
+ {
+ uart_disabletxint(serdev);
+ }
+
+ /* If any bytes were removed from the buffer, inform any waiters
+ * there there is space available.
+ */
+
+ if (nbytes)
+ {
+ uart_datasent(serdev);
+ }
+
+ irqrestore(flags);
+ return nbytes;
+}
+
+/****************************************************************************
+ * Name: cdcacm_sndpacket
+ *
+ * Description:
+ * This function obtains write requests, transfers the TX data into the
+ * request, and submits the requests to the USB controller. This continues
+ * untils either (1) there are no further packets available, or (2) thre is
+ * no further data to send.
+ *
+ ****************************************************************************/
+
+static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
+{
+ FAR struct usbdev_ep_s *ep;
+ FAR struct usbdev_req_s *req;
+ FAR struct cdcacm_req_s *reqcontainer;
+ uint16_t reqlen;
+ irqstate_t flags;
+ int len;
+ int ret = OK;
+
+#ifdef CONFIG_DEBUG
+ if (priv == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return -ENODEV;
+ }
+#endif
+
+ flags = irqsave();
+
+ /* Use our IN endpoint for the transfer */
+
+ ep = priv->epbulkin;
+
+ /* Loop until either (1) we run out or write requests, or (2) cdcacm_fillrequest()
+ * is unable to fill the request with data (i.e., until there is no more data
+ * to be sent).
+ */
+
+ uvdbg("head=%d tail=%d nwrq=%d empty=%d\n",
+ priv->serdev.xmit.head, priv->serdev.xmit.tail,
+ priv->nwrq, sq_empty(&priv->reqlist));
+
+ /* Get the maximum number of bytes that will fit into one bulk IN request */
+
+#ifdef CONFIG_CDCACM_BULKREQLEN
+ reqlen = MAX(CONFIG_CDCACM_BULKREQLEN, ep->maxpacket);
+#else
+ reqlen = ep->maxpacket;
+#endif
+
+ while (!sq_empty(&priv->reqlist))
+ {
+ /* Peek at the request in the container at the head of the list */
+
+ reqcontainer = (struct cdcacm_req_s *)sq_peek(&priv->reqlist);
+ req = reqcontainer->req;
+
+ /* Fill the request with serial TX data */
+
+ len = cdcacm_fillrequest(priv, req->buf, reqlen);
+ if (len > 0)
+ {
+ /* Remove the empty container from the request list */
+
+ (void)sq_remfirst(&priv->reqlist);
+ priv->nwrq--;
+
+ /* Then submit the request to the endpoint */
+
+ req->len = len;
+ req->priv = reqcontainer;
+ req->flags = USBDEV_REQFLAGS_NULLPKT;
+ ret = EP_SUBMIT(ep, req);
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SUBMITFAIL), (uint16_t)-ret);
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ irqrestore(flags);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: cdcacm_recvpacket
+ *
+ * Description:
+ * A normal completion event was received by the read completion handler
+ * at the interrupt level (with interrupts disabled). This function handles
+ * the USB packet and provides the received data to the uart RX buffer.
+ *
+ * Assumptions:
+ * Called from the USB interrupt handler with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static inline int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
+ uint8_t *reqbuf, uint16_t reqlen)
+{
+ FAR uart_dev_t *serdev = &priv->serdev;
+ FAR struct uart_buffer_s *recv = &serdev->recv;
+ uint16_t currhead;
+ uint16_t nexthead;
+ uint16_t nbytes = 0;
+
+ uvdbg("head=%d tail=%d nrdq=%d reqlen=%d\n",
+ priv->serdev.recv.head, priv->serdev.recv.tail, priv->nrdq, reqlen);
+
+ /* Get the next head index. During the time that RX interrupts are disabled, the
+ * the serial driver will be extracting data from the circular buffer and modifying
+ * recv.tail. During this time, we should avoid modifying recv.head; Instead we will
+ * use a shadow copy of the index. When interrupts are restored, the real recv.head
+ * will be updated with this indes.
+ */
+
+ if (priv->rxenabled)
+ {
+ currhead = recv->head;
+ }
+ else
+ {
+ currhead = priv->rxhead;
+ }
+
+ /* Pre-calculate the head index and check for wrap around. We need to do this
+ * so that we can determine if the circular buffer will overrun BEFORE we
+ * overrun the buffer!
+ */
+
+ nexthead = currhead + 1;
+ if (nexthead >= recv->size)
+ {
+ nexthead = 0;
+ }
+
+ /* Then copy data into the RX buffer until either: (1) all of the data has been
+ * copied, or (2) the RX buffer is full. NOTE: If the RX buffer becomes full,
+ * then we have overrun the serial driver and data will be lost.
+ */
+
+ while (nexthead != recv->tail && nbytes < reqlen)
+ {
+ /* Copy one byte to the head of the circular RX buffer */
+
+ recv->buffer[currhead] = *reqbuf++;
+
+ /* Update counts and indices */
+
+ currhead = nexthead;
+ nbytes++;
+
+ /* Increment the head index and check for wrap around */
+
+ nexthead = currhead + 1;
+ if (nexthead >= recv->size)
+ {
+ nexthead = 0;
+ }
+ }
+
+ /* Write back the head pointer using the shadow index if RX "interrupts"
+ * are disabled.
+ */
+
+ if (priv->rxenabled)
+ {
+ recv->head = currhead;
+ }
+ else
+ {
+ priv->rxhead = currhead;
+ }
+
+ /* If data was added to the incoming serial buffer, then wake up any
+ * threads is waiting for incoming data. If we are running in an interrupt
+ * handler, then the serial driver will not run until the interrupt handler
+ * returns.
+ */
+
+ if (priv->rxenabled && nbytes > 0)
+ {
+ uart_datareceived(serdev);
+ }
+
+ /* Return an error if the entire packet could not be transferred */
+
+ if (nbytes < reqlen)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RXOVERRUN), 0);
+ return -ENOSPC;
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: cdcacm_allocreq
+ *
+ * Description:
+ * Allocate a request instance along with its buffer
+ *
+ ****************************************************************************/
+
+static struct usbdev_req_s *cdcacm_allocreq(FAR struct usbdev_ep_s *ep,
+ uint16_t len)
+{
+ FAR struct usbdev_req_s *req;
+
+ req = EP_ALLOCREQ(ep);
+ if (req != NULL)
+ {
+ req->len = len;
+ req->buf = EP_ALLOCBUFFER(ep, len);
+ if (!req->buf)
+ {
+ EP_FREEREQ(ep, req);
+ req = NULL;
+ }
+ }
+ return req;
+}
+
+/****************************************************************************
+ * Name: cdcacm_freereq
+ *
+ * Description:
+ * Free a request instance along with its buffer
+ *
+ ****************************************************************************/
+
+static void cdcacm_freereq(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ if (ep != NULL && req != NULL)
+ {
+ if (req->buf != NULL)
+ {
+ EP_FREEBUFFER(ep, req->buf);
+ }
+ EP_FREEREQ(ep, req);
+ }
+}
+
+/****************************************************************************
+ * Name: cdcacm_resetconfig
+ *
+ * Description:
+ * Mark the device as not configured and disable all endpoints.
+ *
+ ****************************************************************************/
+
+static void cdcacm_resetconfig(FAR struct cdcacm_dev_s *priv)
+{
+ /* Are we configured? */
+
+ if (priv->config != CDCACM_CONFIGIDNONE)
+ {
+ /* Yes.. but not anymore */
+
+ priv->config = CDCACM_CONFIGIDNONE;
+
+ /* Disable endpoints. This should force completion of all pending
+ * transfers.
+ */
+
+ EP_DISABLE(priv->epintin);
+ EP_DISABLE(priv->epbulkin);
+ EP_DISABLE(priv->epbulkout);
+ }
+}
+
+/****************************************************************************
+ * Name: cdcacm_epconfigure
+ *
+ * Description:
+ * Configure one endpoint.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+static int cdcacm_epconfigure(FAR struct usbdev_ep_s *ep,
+ enum cdcacm_epdesc_e epid, uint16_t mxpacket,
+ bool last)
+{
+ struct usb_epdesc_s epdesc;
+ cdcacm_mkepdesc(epid, mxpacket, &epdesc);
+ return EP_CONFIGURE(ep, &epdesc, last);
+}
+#endif
+
+/****************************************************************************
+ * Name: cdcacm_setconfig
+ *
+ * Description:
+ * Set the device configuration by allocating and configuring endpoints and
+ * by allocating and queue read and write requests.
+ *
+ ****************************************************************************/
+
+static int cdcacm_setconfig(FAR struct cdcacm_dev_s *priv, uint8_t config)
+{
+ FAR struct usbdev_req_s *req;
+ int i;
+ int ret = 0;
+
+#if CONFIG_DEBUG
+ if (priv == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return -EIO;
+ }
+#endif
+
+ if (config == priv->config)
+ {
+ /* Already configured -- Do nothing */
+
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALREADYCONFIGURED), 0);
+ return 0;
+ }
+
+ /* Discard the previous configuration data */
+
+ cdcacm_resetconfig(priv);
+
+ /* Was this a request to simply discard the current configuration? */
+
+ if (config == CDCACM_CONFIGIDNONE)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONFIGNONE), 0);
+ return 0;
+ }
+
+ /* We only accept one configuration */
+
+ if (config != CDCACM_CONFIGID)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONFIGIDBAD), 0);
+ return -EINVAL;
+ }
+
+ /* Configure the IN interrupt endpoint */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ if (priv->usbdev->speed == USB_SPEED_HIGH)
+ {
+ ret = cdcacm_epconfigure(priv->epintin, CDCACM_EPINTIN,
+ CONFIG_CDCACM_EPINTIN_HSSIZE, false);
+ }
+ else
+#endif
+ {
+ ret = EP_CONFIGURE(priv->epintin,
+ cdcacm_getepdesc(CDCACM_EPINTIN), false);
+ }
+
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPINTINCONFIGFAIL), 0);
+ goto errout;
+ }
+ priv->epintin->priv = priv;
+
+ /* Configure the IN bulk endpoint */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ if (priv->usbdev->speed == USB_SPEED_HIGH)
+ {
+ ret = cdcacm_epconfigure(priv->epbulkin, CDCACM_EPBULKIN,
+ CONFIG_CDCACM_EPBULKIN_HSSIZE, false);
+ }
+ else
+#endif
+ {
+ ret = EP_CONFIGURE(priv->epbulkin,
+ cdcacm_getepdesc(CDCACM_EPBULKIN), false);
+ }
+
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKINCONFIGFAIL), 0);
+ goto errout;
+ }
+
+ priv->epbulkin->priv = priv;
+
+ /* Configure the OUT bulk endpoint */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ if (priv->usbdev->speed == USB_SPEED_HIGH)
+ {
+ ret = cdcacm_epconfigure(priv->epbulkout, CDCACM_EPBULKOUT,
+ CONFIG_CDCACM_EPBULKOUT_HSSIZE, true);
+ }
+ else
+#endif
+ {
+ ret = EP_CONFIGURE(priv->epbulkout,
+ cdcacm_getepdesc(CDCACM_EPBULKOUT), true);
+ }
+
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKOUTCONFIGFAIL), 0);
+ goto errout;
+ }
+
+ priv->epbulkout->priv = priv;
+
+ /* Queue read requests in the bulk OUT endpoint */
+
+ DEBUGASSERT(priv->nrdq == 0);
+ for (i = 0; i < CONFIG_CDCACM_NRDREQS; i++)
+ {
+ req = priv->rdreqs[i].req;
+ req->callback = cdcacm_rdcomplete;
+ ret = EP_SUBMIT(priv->epbulkout, req);
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16_t)-ret);
+ goto errout;
+ }
+ priv->nrdq++;
+ }
+
+ priv->config = config;
+ return OK;
+
+errout:
+ cdcacm_resetconfig(priv);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: cdcacm_ep0incomplete
+ *
+ * Description:
+ * Handle completion of EP0 control operations
+ *
+ ****************************************************************************/
+
+static void cdcacm_ep0incomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ if (req->result || req->xfrd != req->len)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_REQRESULT), (uint16_t)-req->result);
+ }
+}
+
+/****************************************************************************
+ * Name: cdcacm_rdcomplete
+ *
+ * Description:
+ * Handle completion of read request on the bulk OUT endpoint. This
+ * is handled like the receipt of serial data on the "UART"
+ *
+ ****************************************************************************/
+
+static void cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ FAR struct cdcacm_dev_s *priv;
+ irqstate_t flags;
+ int ret;
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (!ep || !ep->priv || !req)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract references to private data */
+
+ priv = (FAR struct cdcacm_dev_s*)ep->priv;
+
+ /* Process the received data unless this is some unusual condition */
+
+ flags = irqsave();
+ switch (req->result)
+ {
+ case 0: /* Normal completion */
+ usbtrace(TRACE_CLASSRDCOMPLETE, priv->nrdq);
+ cdcacm_recvpacket(priv, req->buf, req->xfrd);
+ break;
+
+ case -ESHUTDOWN: /* Disconnection */
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0);
+ priv->nrdq--;
+ irqrestore(flags);
+ return;
+
+ default: /* Some other error occurred */
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDUNEXPECTED), (uint16_t)-req->result);
+ break;
+ };
+
+ /* Requeue the read request */
+
+#ifdef CONFIG_CDCACM_BULKREQLEN
+ req->len = MAX(CONFIG_CDCACM_BULKREQLEN, ep->maxpacket);
+#else
+ req->len = ep->maxpacket;
+#endif
+
+ ret = EP_SUBMIT(ep, req);
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16_t)-req->result);
+ }
+ irqrestore(flags);
+}
+
+/****************************************************************************
+ * Name: cdcacm_wrcomplete
+ *
+ * Description:
+ * Handle completion of write request. This function probably executes
+ * in the context of an interrupt handler.
+ *
+ ****************************************************************************/
+
+static void cdcacm_wrcomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ FAR struct cdcacm_dev_s *priv;
+ FAR struct cdcacm_req_s *reqcontainer;
+ irqstate_t flags;
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (!ep || !ep->priv || !req || !req->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract references to our private data */
+
+ priv = (FAR struct cdcacm_dev_s *)ep->priv;
+ reqcontainer = (FAR struct cdcacm_req_s *)req->priv;
+
+ /* Return the write request to the free list */
+
+ flags = irqsave();
+ sq_addlast((sq_entry_t*)reqcontainer, &priv->reqlist);
+ priv->nwrq++;
+ irqrestore(flags);
+
+ /* Send the next packet unless this was some unusual termination
+ * condition
+ */
+
+ switch (req->result)
+ {
+ case OK: /* Normal completion */
+ usbtrace(TRACE_CLASSWRCOMPLETE, priv->nwrq);
+ cdcacm_sndpacket(priv);
+ break;
+
+ case -ESHUTDOWN: /* Disconnection */
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRSHUTDOWN), priv->nwrq);
+ break;
+
+ default: /* Some other error occurred */
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRUNEXPECTED), (uint16_t)-req->result);
+ break;
+ }
+}
+
+/****************************************************************************
+ * USB Class Driver Methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cdcacm_bind
+ *
+ * Description:
+ * Invoked when the driver is bound to a USB device driver
+ *
+ ****************************************************************************/
+
+static int cdcacm_bind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct cdcacm_dev_s *priv = ((FAR struct cdcacm_driver_s*)driver)->dev;
+ FAR struct cdcacm_req_s *reqcontainer;
+ irqstate_t flags;
+ uint16_t reqlen;
+ int ret;
+ int i;
+
+ usbtrace(TRACE_CLASSBIND, 0);
+
+ /* Bind the structures */
+
+ priv->usbdev = dev;
+
+ /* Save the reference to our private data structure in EP0 so that it
+ * can be recovered in ep0 completion events (Unless we are part of
+ * a composite device and, in that case, the composite device owns
+ * EP0).
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ dev->ep0->priv = priv;
+#endif
+
+ /* Preallocate control request */
+
+ priv->ctrlreq = cdcacm_allocreq(dev->ep0, CDCACM_MXDESCLEN);
+ if (priv->ctrlreq == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALLOCCTRLREQ), 0);
+ ret = -ENOMEM;
+ goto errout;
+ }
+ priv->ctrlreq->callback = cdcacm_ep0incomplete;
+
+ /* Pre-allocate all endpoints... the endpoints will not be functional
+ * until the SET CONFIGURATION request is processed in cdcacm_setconfig.
+ * This is done here because there may be calls to kmalloc and the SET
+ * CONFIGURATION processing probably occurrs within interrupt handling
+ * logic where kmalloc calls will fail.
+ */
+
+ /* Pre-allocate the IN interrupt endpoint */
+
+ priv->epintin = DEV_ALLOCEP(dev, CDCACM_EPINTIN_ADDR, true, USB_EP_ATTR_XFER_INT);
+ if (!priv->epintin)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPINTINALLOCFAIL), 0);
+ ret = -ENODEV;
+ goto errout;
+ }
+ priv->epintin->priv = priv;
+
+ /* Pre-allocate the IN bulk endpoint */
+
+ priv->epbulkin = DEV_ALLOCEP(dev, CDCACM_EPINBULK_ADDR, true, USB_EP_ATTR_XFER_BULK);
+ if (!priv->epbulkin)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKINALLOCFAIL), 0);
+ ret = -ENODEV;
+ goto errout;
+ }
+ priv->epbulkin->priv = priv;
+
+ /* Pre-allocate the OUT bulk endpoint */
+
+ priv->epbulkout = DEV_ALLOCEP(dev, CDCACM_EPOUTBULK_ADDR, false, USB_EP_ATTR_XFER_BULK);
+ if (!priv->epbulkout)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKOUTALLOCFAIL), 0);
+ ret = -ENODEV;
+ goto errout;
+ }
+ priv->epbulkout->priv = priv;
+
+ /* Pre-allocate read requests */
+
+#ifdef CONFIG_CDCACM_BULKREQLEN
+ reqlen = MAX(CONFIG_CDCACM_BULKREQLEN, priv->epbulkout->maxpacket);
+#else
+ reqlen = priv->epbulkout->maxpacket;
+#endif
+
+ for (i = 0; i < CONFIG_CDCACM_NRDREQS; i++)
+ {
+ reqcontainer = &priv->rdreqs[i];
+ reqcontainer->req = cdcacm_allocreq(priv->epbulkout, reqlen);
+ if (reqcontainer->req == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDALLOCREQ), -ENOMEM);
+ ret = -ENOMEM;
+ goto errout;
+ }
+ reqcontainer->req->priv = reqcontainer;
+ reqcontainer->req->callback = cdcacm_rdcomplete;
+ }
+
+ /* Pre-allocate write request containers and put in a free list */
+
+#ifdef CONFIG_CDCACM_BULKREQLEN
+ reqlen = MAX(CONFIG_CDCACM_BULKREQLEN, priv->epbulkin->maxpacket);
+#else
+ reqlen = priv->epbulkin->maxpacket;
+#endif
+
+ for (i = 0; i < CONFIG_CDCACM_NWRREQS; i++)
+ {
+ reqcontainer = &priv->wrreqs[i];
+ reqcontainer->req = cdcacm_allocreq(priv->epbulkin, reqlen);
+ if (reqcontainer->req == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRALLOCREQ), -ENOMEM);
+ ret = -ENOMEM;
+ goto errout;
+ }
+ reqcontainer->req->priv = reqcontainer;
+ reqcontainer->req->callback = cdcacm_wrcomplete;
+
+ flags = irqsave();
+ sq_addlast((sq_entry_t*)reqcontainer, &priv->reqlist);
+ priv->nwrq++; /* Count of write requests available */
+ irqrestore(flags);
+ }
+
+ /* Report if we are selfpowered (unless we are part of a composite device) */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+#ifdef CONFIG_USBDEV_SELFPOWERED
+ DEV_SETSELFPOWERED(dev);
+#endif
+
+ /* And pull-up the data line for the soft connect function (unless we are
+ * part of a composite device)
+ */
+
+ DEV_CONNECT(dev);
+#endif
+ return OK;
+
+errout:
+ cdcacm_unbind(driver, dev);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: cdcacm_unbind
+ *
+ * Description:
+ * Invoked when the driver is unbound from a USB device driver
+ *
+ ****************************************************************************/
+
+static void cdcacm_unbind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct cdcacm_dev_s *priv;
+ FAR struct cdcacm_req_s *reqcontainer;
+ irqstate_t flags;
+ int i;
+
+ usbtrace(TRACE_CLASSUNBIND, 0);
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = ((FAR struct cdcacm_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
+ return;
+ }
+#endif
+
+ /* Make sure that we are not already unbound */
+
+ if (priv != NULL)
+ {
+ /* Make sure that the endpoints have been unconfigured. If
+ * we were terminated gracefully, then the configuration should
+ * already have been reset. If not, then calling cdcacm_resetconfig
+ * should cause the endpoints to immediately terminate all
+ * transfers and return the requests to us (with result == -ESHUTDOWN)
+ */
+
+ cdcacm_resetconfig(priv);
+ up_mdelay(50);
+
+ /* Free the interrupt IN endpoint */
+
+ if (priv->epintin)
+ {
+ DEV_FREEEP(dev, priv->epintin);
+ priv->epintin = NULL;
+ }
+
+ /* Free the bulk IN endpoint */
+
+ if (priv->epbulkin)
+ {
+ DEV_FREEEP(dev, priv->epbulkin);
+ priv->epbulkin = NULL;
+ }
+
+ /* Free the pre-allocated control request */
+
+ if (priv->ctrlreq != NULL)
+ {
+ cdcacm_freereq(dev->ep0, priv->ctrlreq);
+ priv->ctrlreq = NULL;
+ }
+
+ /* Free pre-allocated read requests (which should all have
+ * been returned to the free list at this time -- we don't check)
+ */
+
+ DEBUGASSERT(priv->nrdq == 0);
+ for (i = 0; i < CONFIG_CDCACM_NRDREQS; i++)
+ {
+ reqcontainer = &priv->rdreqs[i];
+ if (reqcontainer->req)
+ {
+ cdcacm_freereq(priv->epbulkout, reqcontainer->req);
+ reqcontainer->req = NULL;
+ }
+ }
+
+ /* Free the bulk OUT endpoint */
+
+ if (priv->epbulkout)
+ {
+ DEV_FREEEP(dev, priv->epbulkout);
+ priv->epbulkout = NULL;
+ }
+
+ /* Free write requests that are not in use (which should be all
+ * of them)
+ */
+
+ flags = irqsave();
+ DEBUGASSERT(priv->nwrq == CONFIG_CDCACM_NWRREQS);
+ while (!sq_empty(&priv->reqlist))
+ {
+ reqcontainer = (struct cdcacm_req_s *)sq_remfirst(&priv->reqlist);
+ if (reqcontainer->req != NULL)
+ {
+ cdcacm_freereq(priv->epbulkin, reqcontainer->req);
+ priv->nwrq--; /* Number of write requests queued */
+ }
+ }
+ DEBUGASSERT(priv->nwrq == 0);
+ irqrestore(flags);
+ }
+
+ /* Clear out all data in the circular buffer */
+
+ priv->serdev.xmit.head = 0;
+ priv->serdev.xmit.tail = 0;
+}
+
+/****************************************************************************
+ * Name: cdcacm_setup
+ *
+ * Description:
+ * Invoked for ep0 control requests. This function probably executes
+ * in the context of an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int cdcacm_setup(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl,
+ FAR uint8_t *dataout, size_t outlen)
+{
+ FAR struct cdcacm_dev_s *priv;
+ FAR struct usbdev_req_s *ctrlreq;
+ uint16_t value;
+ uint16_t index;
+ uint16_t len;
+ int ret = -EOPNOTSUPP;
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev || !ctrl)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return -EIO;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ usbtrace(TRACE_CLASSSETUP, ctrl->req);
+ priv = ((FAR struct cdcacm_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv || !priv->ctrlreq)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
+ return -ENODEV;
+ }
+#endif
+ ctrlreq = priv->ctrlreq;
+
+ /* Extract the little-endian 16-bit values to host order */
+
+ value = GETUINT16(ctrl->value);
+ index = GETUINT16(ctrl->index);
+ len = GETUINT16(ctrl->len);
+
+ uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
+ ctrl->type, ctrl->req, value, index, len);
+
+ if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD)
+ {
+ /***********************************************************************
+ * Standard Requests
+ ***********************************************************************/
+
+ switch (ctrl->req)
+ {
+ case USB_REQ_GETDESCRIPTOR:
+ {
+ /* The value field specifies the descriptor type in the MS byte and the
+ * descriptor index in the LS byte (order is little endian)
+ */
+
+ switch (ctrl->value[1])
+ {
+ /* If the serial device is used in as part of a composite device,
+ * then the device descriptor is provided by logic in the composite
+ * device implementation.
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+ case USB_DESC_TYPE_DEVICE:
+ {
+ ret = USB_SIZEOF_DEVDESC;
+ memcpy(ctrlreq->buf, cdcacm_getdevdesc(), ret);
+ }
+ break;
+#endif
+
+ /* If the serial device is used in as part of a composite device,
+ * then the device qualifier descriptor is provided by logic in the
+ * composite device implementation.
+ */
+
+#if !defined(CONFIG_CDCACM_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED)
+ case USB_DESC_TYPE_DEVICEQUALIFIER:
+ {
+ ret = USB_SIZEOF_QUALDESC;
+ memcpy(ctrlreq->buf, cdcacm_getqualdesc(), ret);
+ }
+ break;
+
+ case USB_DESC_TYPE_OTHERSPEEDCONFIG:
+#endif
+
+ /* If the serial device is used in as part of a composite device,
+ * then the configuration descriptor is provided by logic in the
+ * composite device implementation.
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+ case USB_DESC_TYPE_CONFIG:
+ {
+#ifdef CONFIG_USBDEV_DUALSPEED
+ ret = cdcacm_mkcfgdesc(ctrlreq->buf, dev->speed, ctrl->req);
+#else
+ ret = cdcacm_mkcfgdesc(ctrlreq->buf);
+#endif
+ }
+ break;
+#endif
+
+ /* If the serial device is used in as part of a composite device,
+ * then the language string descriptor is provided by logic in the
+ * composite device implementation.
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+ case USB_DESC_TYPE_STRING:
+ {
+ /* index == language code. */
+
+ ret = cdcacm_mkstrdesc(ctrl->value[0], (struct usb_strdesc_s *)ctrlreq->buf);
+ }
+ break;
+#endif
+
+ default:
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_GETUNKNOWNDESC), value);
+ }
+ break;
+ }
+ }
+ break;
+
+ case USB_REQ_SETCONFIGURATION:
+ {
+ if (ctrl->type == 0)
+ {
+ ret = cdcacm_setconfig(priv, value);
+ }
+ }
+ break;
+
+ /* If the serial device is used in as part of a composite device,
+ * then the overall composite class configuration is managed by logic
+ * in the composite device implementation.
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+ case USB_REQ_GETCONFIGURATION:
+ {
+ if (ctrl->type == USB_DIR_IN)
+ {
+ *(uint8_t*)ctrlreq->buf = priv->config;
+ ret = 1;
+ }
+ }
+ break;
+#endif
+
+ case USB_REQ_SETINTERFACE:
+ {
+ if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE &&
+ priv->config == CDCACM_CONFIGID)
+ {
+ if ((index == CDCACM_NOTIFID && value == CDCACM_NOTALTIFID) ||
+ (index == CDCACM_DATAIFID && value == CDCACM_DATAALTIFID))
+ {
+ cdcacm_resetconfig(priv);
+ cdcacm_setconfig(priv, priv->config);
+ ret = 0;
+ }
+ }
+ }
+ break;
+
+ case USB_REQ_GETINTERFACE:
+ {
+ if (ctrl->type == (USB_DIR_IN|USB_REQ_RECIPIENT_INTERFACE) &&
+ priv->config == CDCACM_CONFIGIDNONE)
+ {
+ if ((index == CDCACM_NOTIFID && value == CDCACM_NOTALTIFID) ||
+ (index == CDCACM_DATAIFID && value == CDCACM_DATAALTIFID))
+ {
+ *(uint8_t*) ctrlreq->buf = value;
+ ret = 1;
+ }
+ else
+ {
+ ret = -EDOM;
+ }
+ }
+ }
+ break;
+
+ default:
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req);
+ break;
+ }
+ }
+
+ else if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS)
+ {
+ /***********************************************************************
+ * CDC ACM-Specific Requests
+ ***********************************************************************/
+
+ switch (ctrl->req)
+ {
+ /* ACM_GET_LINE_CODING requests current DTE rate, stop-bits, parity, and
+ * number-of-character bits. (Optional)
+ */
+
+ case ACM_GET_LINE_CODING:
+ {
+ if (ctrl->type == (USB_DIR_IN|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE) &&
+ index == CDCACM_NOTIFID)
+ {
+ /* Return the current line status from the private data structure */
+
+ memcpy(ctrlreq->buf, &priv->linecoding, SIZEOF_CDC_LINECODING);
+ ret = SIZEOF_CDC_LINECODING;
+ }
+ else
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type);
+ }
+ }
+ break;
+
+ /* ACM_SET_LINE_CODING configures DTE rate, stop-bits, parity, and
+ * number-of-character bits. (Optional)
+ */
+
+ case ACM_SET_LINE_CODING:
+ {
+ if (ctrl->type == (USB_DIR_OUT|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE) &&
+ len == SIZEOF_CDC_LINECODING && /* dataout && len == outlen && */
+ index == CDCACM_NOTIFID)
+ {
+ /* Save the new line coding in the private data structure. NOTE:
+ * that this is conditional now because not all device controller
+ * drivers supported provisioni of EP0 OUT data with the setup
+ * command.
+ */
+
+ if (dataout && len <= SIZEOF_CDC_LINECODING) /* REVISIT */
+ {
+ memcpy(&priv->linecoding, dataout, SIZEOF_CDC_LINECODING);
+ }
+ ret = 0;
+
+ /* If there is a registered callback to receive line status info, then
+ * callout now.
+ */
+
+ if (priv->callback)
+ {
+ priv->callback(CDCACM_EVENT_LINECODING);
+ }
+ }
+ else
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type);
+ }
+ }
+ break;
+
+ /* ACM_SET_CTRL_LINE_STATE: RS-232 signal used to tell the DCE device the
+ * DTE device is now present. (Optional)
+ */
+
+ case ACM_SET_CTRL_LINE_STATE:
+ {
+ if (ctrl->type == (USB_DIR_OUT|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE) &&
+ index == CDCACM_NOTIFID)
+ {
+ /* Save the control line state in the private data structure. Only bits
+ * 0 and 1 have meaning.
+ */
+
+ priv->ctrlline = value & 3;
+ ret = 0;
+
+ /* If there is a registered callback to receive control line status info,
+ * then callout now.
+ */
+
+ if (priv->callback)
+ {
+ priv->callback(CDCACM_EVENT_CTRLLINE);
+ }
+ }
+ else
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type);
+ }
+ }
+ break;
+
+ /* Sends special carrier*/
+
+ case ACM_SEND_BREAK:
+ {
+ if (ctrl->type == (USB_DIR_OUT|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE) &&
+ index == CDCACM_NOTIFID)
+ {
+ /* If there is a registered callback to handle the SendBreak request,
+ * then callout now.
+ */
+
+ ret = 0;
+ if (priv->callback)
+ {
+ priv->callback(CDCACM_EVENT_SENDBREAK);
+ }
+ }
+ else
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type);
+ }
+ }
+ break;
+
+ default:
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->req);
+ break;
+ }
+ }
+ else
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDTYPE), ctrl->type);
+ }
+
+ /* Respond to the setup command if data was returned. On an error return
+ * value (ret < 0), the USB driver will stall.
+ */
+
+ if (ret >= 0)
+ {
+ /* Configure the response */
+
+ ctrlreq->len = MIN(len, ret);
+ ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;
+
+ /* Send the response -- either directly to the USB controller or
+ * indirectly in the case where this class is a member of a composite
+ * device.
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+ ret = EP_SUBMIT(dev->ep0, ctrlreq);
+#else
+ ret = composite_ep0submit(driver, dev, ctrlreq);
+#endif
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPRESPQ), (uint16_t)-ret);
+ ctrlreq->result = OK;
+ cdcacm_ep0incomplete(dev->ep0, ctrlreq);
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: cdcacm_disconnect
+ *
+ * Description:
+ * Invoked after all transfers have been stopped, when the host is
+ * disconnected. This function is probably called from the context of an
+ * interrupt handler.
+ *
+ ****************************************************************************/
+
+static void cdcacm_disconnect(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct cdcacm_dev_s *priv;
+ irqstate_t flags;
+
+ usbtrace(TRACE_CLASSDISCONNECT, 0);
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev || !dev->ep0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = ((FAR struct cdcacm_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
+ return;
+ }
+#endif
+
+ /* Reset the configuration */
+
+ flags = irqsave();
+ cdcacm_resetconfig(priv);
+
+ /* Clear out all data in the circular buffer */
+
+ priv->serdev.xmit.head = 0;
+ priv->serdev.xmit.tail = 0;
+ irqrestore(flags);
+
+ /* Perform the soft connect function so that we will we can be
+ * re-enumerated (unless we are part of a composite device)
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+ DEV_CONNECT(dev);
+#endif
+}
+
+/****************************************************************************
+ * Serial Device Methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cdcuart_setup
+ *
+ * Description:
+ * This method is called the first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int cdcuart_setup(FAR struct uart_dev_s *dev)
+{
+ FAR struct cdcacm_dev_s *priv;
+
+ usbtrace(CDCACM_CLASSAPI_SETUP, 0);
+
+ /* Sanity check */
+
+#if CONFIG_DEBUG
+ if (!dev || !dev->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return -EIO;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = (FAR struct cdcacm_dev_s*)dev->priv;
+
+ /* Check if we have been configured */
+
+ if (priv->config == CDCACM_CONFIGIDNONE)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SETUPNOTCONNECTED), 0);
+ return -ENOTCONN;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: cdcuart_shutdown
+ *
+ * Description:
+ * This method is called when the serial port is closed. This operation
+ * is very simple for the USB serial backend because the serial driver
+ * has already assured that the TX data has full drained -- it calls
+ * cdcuart_txempty() until that function returns true before calling this
+ * function.
+ *
+ ****************************************************************************/
+
+static void cdcuart_shutdown(FAR struct uart_dev_s *dev)
+{
+ usbtrace(CDCACM_CLASSAPI_SHUTDOWN, 0);
+
+ /* Sanity check */
+
+#if CONFIG_DEBUG
+ if (!dev || !dev->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ }
+#endif
+}
+
+/****************************************************************************
+ * Name: cdcuart_attach
+ *
+ * Description:
+ * Does not apply to the USB serial class device
+ *
+ ****************************************************************************/
+
+static int cdcuart_attach(FAR struct uart_dev_s *dev)
+{
+ usbtrace(CDCACM_CLASSAPI_ATTACH, 0);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: cdcuart_detach
+ *
+ * Description:
+* Does not apply to the USB serial class device
+ *
+ ****************************************************************************/
+
+static void cdcuart_detach(FAR struct uart_dev_s *dev)
+{
+ usbtrace(CDCACM_CLASSAPI_DETACH, 0);
+}
+
+/****************************************************************************
+ * Name: cdcuart_ioctl
+ *
+ * Description:
+ * All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int cdcuart_ioctl(FAR struct file *filep,int cmd,unsigned long arg)
+{
+ struct inode *inode = filep->f_inode;
+ struct cdcacm_dev_s *priv = inode->i_private;
+ int ret = OK;
+
+ switch (cmd)
+ {
+ /* CAICO_REGISTERCB
+ * Register a callback for serial event notification. Argument:
+ * cdcacm_callback_t. See cdcacm_callback_t type definition below.
+ * NOTE: The callback will most likely invoked at the interrupt level.
+ * The called back function should, therefore, limit its operations to
+ * invoking some kind of IPC to handle the serial event in some normal
+ * task environment.
+ */
+
+ case CAIOC_REGISTERCB:
+ {
+ /* Save the new callback function */
+
+ priv->callback = (cdcacm_callback_t)((uintptr_t)arg);
+ }
+ break;
+
+ /* CAIOC_GETLINECODING
+ * Get current line coding. Argument: struct cdc_linecoding_s*.
+ * See include/nuttx/usb/cdc.h for structure definition. This IOCTL
+ * should be called to get the data associated with the
+ * CDCACM_EVENT_LINECODING event).
+ */
+
+ case CAIOC_GETLINECODING:
+ {
+ FAR struct cdc_linecoding_s *ptr = (FAR struct cdc_linecoding_s *)((uintptr_t)arg);
+ if (ptr)
+ {
+ memcpy(ptr, &priv->linecoding, sizeof(struct cdc_linecoding_s));
+ }
+ else
+ {
+ ret = -EINVAL;
+ }
+ }
+ break;
+
+ /* CAIOC_GETCTRLLINE
+ * Get control line status bits. Argument FAR int*. See
+ * include/nuttx/usb/cdc.h for bit definitions. This IOCTL should be
+ * called to get the data associated CDCACM_EVENT_CTRLLINE event.
+ */
+
+ case CAIOC_GETCTRLLINE:
+ {
+ FAR int *ptr = (FAR int *)((uintptr_t)arg);
+ if (ptr)
+ {
+ *ptr = priv->ctrlline;
+ }
+ else
+ {
+ ret = -EINVAL;
+ }
+ }
+ break;
+
+ /* CAIOC_NOTIFY
+ * Send a serial state to the host via the Interrupt IN endpoint.
+ * Argument: int. This includes the current state of the carrier detect,
+ * DSR, break, and ring signal. See "Table 69: UART State Bitmap Values"
+ * and CDC_UART_definitions in include/nuttx/usb/cdc.h.
+ */
+
+ case CAIOC_NOTIFY:
+ {
+ /* Not yet implemented. I probably won't bother to implement until
+ * I comr up with a usage model that needs it.
+ *
+ * Here is what the needs to be done:
+ *
+ * 1. Format and send a request header with:
+ *
+ * bmRequestType:
+ * USB_REQ_DIR_IN|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE
+ * bRequest: ACM_SERIAL_STATE
+ * wValue: 0
+ * wIndex: 0
+ * wLength: Length of data
+ *
+ * 2. Followed by the notification data (in a separate packet)
+ */
+
+ ret = -ENOSYS;
+ }
+ break;
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: cdcuart_rxint
+ *
+ * Description:
+ * Called by the serial driver to enable or disable RX interrupts. We, of
+ * course, have no RX interrupts but must behave consistently. This method
+ * is called under the conditions:
+ *
+ * 1. With enable==true when the port is opened (just after cdcuart_setup
+ * and cdcuart_attach are called called)
+ * 2. With enable==false while transferring data from the RX buffer
+ * 2. With enable==true while waiting for more incoming data
+ * 3. With enable==false when the port is closed (just before cdcuart_detach
+ * and cdcuart_shutdown are called).
+ *
+ ****************************************************************************/
+
+static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable)
+{
+ FAR struct cdcacm_dev_s *priv;
+ FAR uart_dev_t *serdev;
+ irqstate_t flags;
+
+ usbtrace(CDCACM_CLASSAPI_RXINT, (uint16_t)enable);
+
+ /* Sanity check */
+
+#if CONFIG_DEBUG
+ if (!dev || !dev->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = (FAR struct cdcacm_dev_s*)dev->priv;
+ serdev = &priv->serdev;
+
+ /* We need exclusive access to the RX buffer and private structure
+ * in the following.
+ */
+
+ flags = irqsave();
+ if (enable)
+ {
+ /* RX "interrupts" are enabled. Is this a transition from disabled
+ * to enabled state?
+ */
+
+ if (!priv->rxenabled)
+ {
+ /* Yes. During the time that RX interrupts are disabled, the
+ * the serial driver will be extracting data from the circular
+ * buffer and modifying recv.tail. During this time, we
+ * should avoid modifying recv.head; When interrupts are restored,
+ * we can update the head pointer for all of the data that we
+ * put into cicular buffer while "interrupts" were disabled.
+ */
+
+ if (priv->rxhead != serdev->recv.head)
+ {
+ serdev->recv.head = priv->rxhead;
+
+ /* Yes... signal the availability of new data */
+
+ uart_datareceived(serdev);
+ }
+
+ /* RX "interrupts are no longer disabled */
+
+ priv->rxenabled = true;
+ }
+ }
+
+ /* RX "interrupts" are disabled. Is this a transition from enabled
+ * to disabled state?
+ */
+
+ else if (priv->rxenabled)
+ {
+ /* Yes. During the time that RX interrupts are disabled, the
+ * the serial driver will be extracting data from the circular
+ * buffer and modifying recv.tail. During this time, we
+ * should avoid modifying recv.head; When interrupts are disabled,
+ * we use a shadow index and continue adding data to the circular
+ * buffer.
+ */
+
+ priv->rxhead = serdev->recv.head;
+ priv->rxenabled = false;
+ }
+ irqrestore(flags);
+}
+
+/****************************************************************************
+ * Name: cdcuart_txint
+ *
+ * Description:
+ * Called by the serial driver to enable or disable TX interrupts. We, of
+ * course, have no TX interrupts but must behave consistently. Initially,
+ * TX interrupts are disabled. This method is called under the conditions:
+ *
+ * 1. With enable==false while transferring data into the TX buffer
+ * 2. With enable==true when data may be taken from the buffer.
+ * 3. With enable==false when the TX buffer is empty
+ *
+ ****************************************************************************/
+
+static void cdcuart_txint(FAR struct uart_dev_s *dev, bool enable)
+{
+ FAR struct cdcacm_dev_s *priv;
+
+ usbtrace(CDCACM_CLASSAPI_TXINT, (uint16_t)enable);
+
+ /* Sanity checks */
+
+#if CONFIG_DEBUG
+ if (!dev || !dev->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract references to private data */
+
+ priv = (FAR struct cdcacm_dev_s*)dev->priv;
+
+ /* If the new state is enabled and if there is data in the XMIT buffer,
+ * send the next packet now.
+ */
+
+ uvdbg("enable=%d head=%d tail=%d\n",
+ enable, priv->serdev.xmit.head, priv->serdev.xmit.tail);
+
+ if (enable && priv->serdev.xmit.head != priv->serdev.xmit.tail)
+ {
+ cdcacm_sndpacket(priv);
+ }
+}
+
+/****************************************************************************
+ * Name: cdcuart_txempty
+ *
+ * Description:
+ * Return true when all data has been sent. This is called from the
+ * serial driver when the driver is closed. It will call this API
+ * periodically until it reports true. NOTE that the serial driver takes all
+ * responsibility for flushing TX data through the hardware so we can be
+ * a bit sloppy about that.
+ *
+ ****************************************************************************/
+
+static bool cdcuart_txempty(FAR struct uart_dev_s *dev)
+{
+ FAR struct cdcacm_dev_s *priv = (FAR struct cdcacm_dev_s*)dev->priv;
+
+ usbtrace(CDCACM_CLASSAPI_TXEMPTY, 0);
+
+#if CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return true;
+ }
+#endif
+
+ /* When all of the allocated write requests have been returned to the
+ * reqlist, then there is no longer any TX data in flight.
+ */
+
+ return priv->nwrq >= CONFIG_CDCACM_NWRREQS;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cdcacm_classobject
+ *
+ * Description:
+ * Register USB serial port (and USB serial console if so configured) and
+ * return the class object.
+ *
+ * Input Parameter:
+ * minor - Device minor number. E.g., minor 0 would correspond to
+ * /dev/ttyACM0.
+ * classdev - The location to return the CDC serial class' device
+ * instance.
+ *
+ * Returned Value:
+ * A pointer to the allocated class object (NULL on failure).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+static
+#endif
+int cdcacm_classobject(int minor, FAR struct usbdevclass_driver_s **classdev)
+{
+ FAR struct cdcacm_alloc_s *alloc;
+ FAR struct cdcacm_dev_s *priv;
+ FAR struct cdcacm_driver_s *drvr;
+ char devname[CDCACM_DEVNAME_SIZE];
+ int ret;
+
+ /* Allocate the structures needed */
+
+ alloc = (FAR struct cdcacm_alloc_s*)kmalloc(sizeof(struct cdcacm_alloc_s));
+ if (!alloc)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALLOCDEVSTRUCT), 0);
+ return -ENOMEM;
+ }
+
+ /* Convenience pointers into the allocated blob */
+
+ priv = &alloc->dev;
+ drvr = &alloc->drvr;
+
+ /* Initialize the USB serial driver structure */
+
+ memset(priv, 0, sizeof(struct cdcacm_dev_s));
+ sq_init(&priv->reqlist);
+
+ priv->minor = minor;
+
+ /* Fake line status */
+
+ priv->linecoding.baud[0] = (115200) & 0xff; /* Baud=115200 */
+ priv->linecoding.baud[1] = (115200 >> 8) & 0xff;
+ priv->linecoding.baud[2] = (115200 >> 16) & 0xff;
+ priv->linecoding.baud[3] = (115200 >> 24) & 0xff;
+ priv->linecoding.stop = CDC_CHFMT_STOP1; /* One stop bit */
+ priv->linecoding.parity = CDC_PARITY_NONE; /* No parity */
+ priv->linecoding.nbits = 8; /* 8 data bits */
+
+ /* Initialize the serial driver sub-structure */
+
+ priv->serdev.recv.size = CONFIG_CDCACM_RXBUFSIZE;
+ priv->serdev.recv.buffer = priv->rxbuffer;
+ priv->serdev.xmit.size = CONFIG_CDCACM_TXBUFSIZE;
+ priv->serdev.xmit.buffer = priv->txbuffer;
+ priv->serdev.ops = &g_uartops;
+ priv->serdev.priv = priv;
+
+ /* Initialize the USB class driver structure */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ drvr->drvr.speed = USB_SPEED_HIGH;
+#else
+ drvr->drvr.speed = USB_SPEED_FULL;
+#endif
+ drvr->drvr.ops = &g_driverops;
+ drvr->dev = priv;
+
+ /* Register the USB serial console */
+
+#ifdef CONFIG_CDCACM_CONSOLE
+ priv->serdev.isconsole = true;
+ ret = uart_register("/dev/console", &priv->serdev);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONSOLEREGISTER), (uint16_t)-ret);
+ goto errout_with_class;
+ }
+#endif
+
+ /* Register the CDC/ACM TTY device */
+
+ sprintf(devname, CDCACM_DEVNAME_FORMAT, minor);
+ ret = uart_register(devname, &priv->serdev);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UARTREGISTER), (uint16_t)-ret);
+ goto errout_with_class;
+ }
+
+ *classdev = &drvr->drvr;
+ return OK;
+
+errout_with_class:
+ kfree(alloc);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: cdcacm_initialize
+ *
+ * Description:
+ * Register USB serial port (and USB serial console if so configured).
+ *
+ * Input Parameter:
+ * minor - Device minor number. E.g., minor 0 would correspond to
+ * /dev/ttyACM0.
+ * handle - An optional opaque reference to the CDC/ACM class object that
+ * may subsequently be used with cdcacm_uninitialize().
+ *
+ * Returned Value:
+ * Zero (OK) means that the driver was successfully registered. On any
+ * failure, a negated errno value is retured.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+int cdcacm_initialize(int minor, FAR void **handle)
+{
+ FAR struct usbdevclass_driver_s *drvr = NULL;
+ int ret;
+
+ /* Get an instance of the serial driver class object */
+
+ ret = cdcacm_classobject(minor, &drvr);
+ if (ret == OK)
+ {
+ /* Register the USB serial class driver */
+
+ ret = usbdev_register(drvr);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_DEVREGISTER), (uint16_t)-ret);
+ }
+ }
+
+ /* Return the driver instance (if any) if the caller has requested it
+ * by provided a pointer to the location to return it.
+ */
+
+ if (handle)
+ {
+ *handle = (FAR void*)drvr;
+ }
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: cdcacm_uninitialize
+ *
+ * Description:
+ * Un-initialize the USB storage class driver. This function is used
+ * internally by the USB composite driver to unitialized the CDC/ACM
+ * driver. This same interface is available (with an untyped input
+ * parameter) when the CDC/ACM driver is used standalone.
+ *
+ * Input Parameters:
+ * There is one parameter, it differs in typing depending upon whether the
+ * CDC/ACM driver is an internal part of a composite device, or a standalone
+ * USB driver:
+ *
+ * classdev - The class object returned by board_cdcclassobject() or
+ * cdcacm_classobject()
+ * handle - The opaque handle represetning the class object returned by
+ * a previous call to cdcacm_initialize().
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_CDCACM_COMPOSITE
+void cdcacm_uninitialize(FAR struct usbdevclass_driver_s *classdev)
+#else
+void cdcacm_uninitialize(FAR void *handle)
+#endif
+{
+#ifdef CONFIG_CDCACM_COMPOSITE
+ FAR struct cdcacm_driver_s *drvr = (FAR struct cdcacm_driver_s *)classdev;
+#else
+ FAR struct cdcacm_driver_s *drvr = (FAR struct cdcacm_driver_s *)handle;
+#endif
+ FAR struct cdcacm_dev_s *priv = drvr->dev;
+ char devname[CDCACM_DEVNAME_SIZE];
+ int ret;
+
+ /* Un-register the CDC/ACM TTY device */
+
+ sprintf(devname, CDCACM_DEVNAME_FORMAT, priv->minor);
+ ret = unregister_driver(devname);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UARTUNREGISTER), (uint16_t)-ret);
+ }
+
+ /* Unregister the driver (unless we are a part of a composite device). The
+ * device unregister logic will (1) return all of the requests to us then
+ * (2) all the unbind method.
+ *
+ * The same thing will happen in the composite case except that: (1) the
+ * composite driver will call usbdev_unregister() which will (2) return the
+ * requests for all members of the composite, and (3) call the unbind
+ * method in the composite device which will (4) call the unbind method
+ * for this device.
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+ usbdev_unregister(&drvr->drvr);
+#endif
+
+ /* And free the driver structure */
+
+ kfree(priv);
+}
diff --git a/nuttx/drivers/usbdev/cdcacm.h b/nuttx/drivers/usbdev/cdcacm.h
new file mode 100644
index 000000000..8b06ca9f3
--- /dev/null
+++ b/nuttx/drivers/usbdev/cdcacm.h
@@ -0,0 +1,350 @@
+/****************************************************************************
+ * drivers/usbdev/cdcacm.h
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_USBDEV_CDCACM_H
+#define __DRIVERS_USBDEV_CDCACM_H 1
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+#include <nuttx/usb/usbdev.h>
+#include <nuttx/usb/cdc.h>
+#include <nuttx/usb/usbdev_trace.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+/* If the serial device is configured as part of a composite device than both
+ * CONFIG_USBDEV_COMPOSITE and CONFIG_CDCACM_COMPOSITE must be defined.
+ */
+
+#ifndef CONFIG_USBDEV_COMPOSITE
+# undef CONFIG_CDCACM_COMPOSITE
+#endif
+
+#if defined(CONFIG_CDCACM_COMPOSITE) && !defined(CONFIG_CDCACM_STRBASE)
+# define CONFIG_CDCACM_STRBASE (4)
+#endif
+
+#if defined(CONFIG_CDCACM_COMPOSITE) && !defined(CONFIG_COMPOSITE_IAD)
+# warning "CONFIG_COMPOSITE_IAD may be needed"
+#endif
+
+/* Packet and request buffer sizes */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+# ifndef CONFIG_CDCACM_EP0MAXPACKET
+# define CONFIG_CDCACM_EP0MAXPACKET 64
+# endif
+#endif
+
+/* Interface IDs. If the serial driver is built as a component of a composite
+ * device, then the interface IDs may need to be offset.
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+# undef CONFIG_CDCACM_IFNOBASE
+# define CONFIG_CDCACM_IFNOBASE 0
+#endif
+
+#ifndef CONFIG_CDCACM_IFNOBASE
+# define CONFIG_CDCACM_IFNOBASE 0
+#endif
+
+/* Descriptors **************************************************************/
+/* These settings are not modifiable via the NuttX configuration */
+
+#define CDC_VERSIONNO 0x0110 /* CDC version number 1.10 (BCD) */
+#define CDCACM_CONFIGIDNONE (0) /* Config ID means to return to address mode */
+
+/* Interface IDs:
+ *
+ * CDCACM_NINTERFACES Two interfaces
+ * CDCACM_NOTIFID ID of the notifier interface
+ * CDCACM_NOTALTIFID No alternate for the notifier interface
+ * CDCACM_DATAIFID ID of the data interface
+ * CDCACM_DATAALTIFID No alternate for the data interface
+ */
+
+#define CDCACM_NINTERFACES (2) /* Number of interfaces in the configuration */
+#define CDCACM_NOTIFID (CONFIG_CDCACM_IFNOBASE+0)
+#define CDCACM_NOTALTIFID (0)
+#define CDCACM_DATAIFID (CONFIG_CDCACM_IFNOBASE+1)
+#define CDCACM_DATAALTIFID (0)
+
+/* Configuration descriptor values */
+
+#define CDCACM_CONFIGID (1) /* The only supported configuration ID */
+
+/* Buffer big enough for any of our descriptors (the config descriptor is the
+ * biggest).
+ */
+
+#define CDCACM_MXDESCLEN (64)
+
+/* Device descriptor values */
+
+#define CDCACM_VERSIONNO (0x0101) /* Device version number 1.1 (BCD) */
+#define CDCACM_NCONFIGS (1) /* Number of configurations supported */
+
+/* String language */
+
+#define CDCACM_STR_LANGUAGE (0x0409) /* en-us */
+
+/* Descriptor strings. If there serial device is part of a composite device
+ * then the manufacturer, product, and serial number strings will be provided
+ * by the composite logic.
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+# define CDCACM_MANUFACTURERSTRID (1)
+# define CDCACM_PRODUCTSTRID (2)
+# define CDCACM_SERIALSTRID (3)
+# define CDCACM_CONFIGSTRID (4)
+
+# define CDCACM_LASTBASESTRID (4)
+# undef CONFIG_CDCACM_STRBASE
+# define CONFIG_CDCACM_STRBASE (0)
+#else
+# define CDCACM_LASTBASESTRID CONFIG_CDCACM_STRBASE
+#endif
+
+/* These string IDs only exist if a user-defined string is provided */
+
+#ifdef CONFIG_CDCACM_NOTIFSTR
+# define CDCACM_NOTIFSTRID (CDCACM_LASTBASESTRID+1)
+#else
+# define CDCACM_NOTIFSTRID CDCACM_LASTBASESTRID
+#endif
+
+#ifdef CONFIG_CDCACM_DATAIFSTR
+# define CDCACM_DATAIFSTRID (CDCACM_NOTIFSTRID+1)
+#else
+# define CDCACM_DATAIFSTRID CDCACM_NOTIFSTRID
+#endif
+
+#define CDCACM_LASTSTRID CDCACM_DATAIFSTRID
+#define CDCACM_NSTRIDS (CDCACM_LASTSTRID - CONFIG_CDCACM_STRBASE)
+
+/* Configuration descriptor size */
+
+#if !defined(CONFIG_CDCACM_COMPOSITE)
+
+/* Number of individual descriptors in the configuration descriptor:
+ * Configuration descriptor + (2) interface descriptors + (3) endpoint
+ * descriptors + (3) ACM descriptors.
+ */
+
+# define CDCACM_CFGGROUP_SIZE (9)
+
+/* The size of the config descriptor: (9 + 2*9 + 3*7 + 4 + 5 + 5) = 62 */
+
+# define SIZEOF_CDCACM_CFGDESC \
+ (USB_SIZEOF_CFGDESC + 2*USB_SIZEOF_IFDESC + 3*USB_SIZEOF_EPDESC + \
+ SIZEOF_ACM_FUNCDESC + SIZEOF_HDR_FUNCDESC + SIZEOF_UNION_FUNCDESC(1))
+
+#elif defined(CONFIG_COMPOSITE_IAD)
+
+/* Number of individual descriptors in the configuration descriptor:
+ * (1) interface association descriptor + (2) interface descriptors +
+ * (3) endpoint descriptors + (3) ACM descriptors.
+ */
+
+# define CDCACM_CFGGROUP_SIZE (9)
+
+/* The size of the config descriptor: (8 + 2*9 + 3*7 + 4 + 5 + 5) = 61 */
+
+# define SIZEOF_CDCACM_CFGDESC \
+ (USB_SIZEOF_IADDESC +2*USB_SIZEOF_IFDESC + 3*USB_SIZEOF_EPDESC + \
+ SIZEOF_ACM_FUNCDESC + SIZEOF_HDR_FUNCDESC + SIZEOF_UNION_FUNCDESC(1))
+
+#else
+
+/* Number of individual descriptors in the configuration descriptor:
+ * (2) interface descriptors + (3) endpoint descriptors + (3) ACM descriptors.
+ */
+
+# define CDCACM_CFGGROUP_SIZE (8)
+
+/* The size of the config descriptor: (2*9 + 3*7 + 4 + 5 + 5) = 53 */
+
+# define SIZEOF_CDCACM_CFGDESC \
+ (2*USB_SIZEOF_IFDESC + 3*USB_SIZEOF_EPDESC + SIZEOF_ACM_FUNCDESC + \
+ SIZEOF_HDR_FUNCDESC + SIZEOF_UNION_FUNCDESC(1))
+#endif
+
+/* Endpoint configuration ****************************************************/
+
+#define CDCACM_EPINTIN_ADDR (USB_DIR_IN|CONFIG_CDCACM_EPINTIN)
+#define CDCACM_EPINTIN_ATTR (USB_EP_ATTR_XFER_INT)
+
+#define CDCACM_EPOUTBULK_ADDR (CONFIG_CDCACM_EPBULKOUT)
+#define CDCACM_EPOUTBULK_ATTR (USB_EP_ATTR_XFER_BULK)
+
+#define CDCACM_EPINBULK_ADDR (USB_DIR_IN|CONFIG_CDCACM_EPBULKIN)
+#define CDCACM_EPINBULK_ATTR (USB_EP_ATTR_XFER_BULK)
+
+/* Device driver definitions ************************************************/
+/* A CDC/ACM device is specific by a minor number in the range of 0-255.
+ * This maps to a character device at /dev/ttyACMx, x=0..255.
+ */
+
+#define CDCACM_DEVNAME_FORMAT "/dev/ttyACM%d"
+#define CDCACM_DEVNAME_SIZE 16
+
+/* Misc Macros **************************************************************/
+/* MIN/MAX macros */
+
+#ifndef MIN
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+/* Trace values *************************************************************/
+
+#define CDCACM_CLASSAPI_SETUP TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_SETUP)
+#define CDCACM_CLASSAPI_SHUTDOWN TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_SHUTDOWN)
+#define CDCACM_CLASSAPI_ATTACH TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_ATTACH)
+#define CDCACM_CLASSAPI_DETACH TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_DETACH)
+#define CDCACM_CLASSAPI_IOCTL TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_IOCTL)
+#define CDCACM_CLASSAPI_RECEIVE TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_RECEIVE)
+#define CDCACM_CLASSAPI_RXINT TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_RXINT)
+#define CDCACM_CLASSAPI_RXAVAILABLE TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_RXAVAILABLE)
+#define CDCACM_CLASSAPI_SEND TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_SEND)
+#define CDCACM_CLASSAPI_TXINT TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_TXINT)
+#define CDCACM_CLASSAPI_TXREADY TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_TXREADY)
+#define CDCACM_CLASSAPI_TXEMPTY TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_TXEMPTY)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+enum cdcacm_epdesc_e
+{
+ CDCACM_EPINTIN = 0, /* Interrupt IN endpoint descriptor */
+ CDCACM_EPBULKOUT, /* Bulk OUT endpoint descriptor */
+ CDCACM_EPBULKIN /* Bulk IN endpoint descriptor */
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cdcacm_mkstrdesc
+ *
+ * Description:
+ * Construct a string descriptor
+ *
+ ****************************************************************************/
+
+int cdcacm_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc);
+
+/****************************************************************************
+ * Name: cdcacm_getepdesc
+ *
+ * Description:
+ * Return a pointer to the raw device descriptor
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+FAR const struct usb_devdesc_s *cdcacm_getdevdesc(void);
+#endif
+
+/****************************************************************************
+ * Name: cdcacm_getepdesc
+ *
+ * Description:
+ * Return a pointer to the raw endpoint descriptor (used for configuring
+ * endpoints)
+ *
+ ****************************************************************************/
+
+FAR const struct usb_epdesc_s *cdcacm_getepdesc(enum cdcacm_epdesc_e epid);
+
+/****************************************************************************
+ * Name: cdcacm_mkepdesc
+ *
+ * Description:
+ * Construct the endpoint descriptor using the correct max packet size.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+void cdcacm_mkepdesc(enum cdcacm_epdesc_e epid,
+ uint16_t mxpacket, FAR struct usb_epdesc_s *outdesc);
+#endif
+
+/****************************************************************************
+ * Name: cdcacm_mkcfgdesc
+ *
+ * Description:
+ * Construct the configuration descriptor
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+int16_t cdcacm_mkcfgdesc(FAR uint8_t *buf, uint8_t speed, uint8_t type);
+#else
+int16_t cdcacm_mkcfgdesc(FAR uint8_t *buf);
+#endif
+
+/****************************************************************************
+ * Name: cdcacm_getqualdesc
+ *
+ * Description:
+ * Return a pointer to the raw qual descriptor
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_CDCACM_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED)
+FAR const struct usb_qualdesc_s *cdcacm_getqualdesc(void);
+#endif
+
+#endif /* __DRIVERS_USBDEV_CDCACM_H */
diff --git a/nuttx/drivers/usbdev/cdcacm_desc.c b/nuttx/drivers/usbdev/cdcacm_desc.c
new file mode 100644
index 000000000..fde13bfd3
--- /dev/null
+++ b/nuttx/drivers/usbdev/cdcacm_desc.c
@@ -0,0 +1,613 @@
+/****************************************************************************
+ * drivers/usbdev/cdcacm_desc.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/cdc.h>
+#include <nuttx/usb/cdcacm.h>
+#include <nuttx/usb/usbdev_trace.h>
+
+#include "cdcacm.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Describes one description in the group of descriptors forming the
+ * total configuration descriptor.
+ */
+
+struct cfgdecsc_group_s
+{
+ uint16_t descsize; /* Size of the descriptor in bytes */
+ uint16_t hsepsize; /* High speed max packet size */
+ FAR void *desc; /* A pointer to the descriptor */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* USB descriptor templates these will be copied and modified **************/
+/* Device Descriptor. If the USB serial device is configured as part of
+ * composite device, then the device descriptor will be provided by the
+ * composite device logic.
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+static const struct usb_devdesc_s g_devdesc =
+{
+ USB_SIZEOF_DEVDESC, /* len */
+ USB_DESC_TYPE_DEVICE, /* type */
+ { /* usb */
+ LSBYTE(0x0200),
+ MSBYTE(0x0200)
+ },
+ USB_CLASS_CDC, /* class */
+ CDC_SUBCLASS_NONE, /* subclass */
+ CDC_PROTO_NONE, /* protocol */
+ CONFIG_CDCACM_EP0MAXPACKET, /* maxpacketsize */
+ {
+ LSBYTE(CONFIG_CDCACM_VENDORID), /* vendor */
+ MSBYTE(CONFIG_CDCACM_VENDORID)
+ },
+ {
+ LSBYTE(CONFIG_CDCACM_PRODUCTID), /* product */
+ MSBYTE(CONFIG_CDCACM_PRODUCTID)
+ },
+ {
+ LSBYTE(CDCACM_VERSIONNO), /* device */
+ MSBYTE(CDCACM_VERSIONNO)
+ },
+ CDCACM_MANUFACTURERSTRID, /* imfgr */
+ CDCACM_PRODUCTSTRID, /* iproduct */
+ CDCACM_SERIALSTRID, /* serno */
+ CDCACM_NCONFIGS /* nconfigs */
+};
+#endif
+
+/* Configuration descriptor. If the USB serial device is configured as part of
+ * composite device, then the configuration descriptor will be provided by the
+ * composite device logic.
+ */
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+static const struct usb_cfgdesc_s g_cfgdesc =
+{
+ USB_SIZEOF_CFGDESC, /* len */
+ USB_DESC_TYPE_CONFIG, /* type */
+ {
+ LSBYTE(SIZEOF_CDCACM_CFGDESC), /* LS totallen */
+ MSBYTE(SIZEOF_CDCACM_CFGDESC) /* MS totallen */
+ },
+ CDCACM_NINTERFACES, /* ninterfaces */
+ CDCACM_CONFIGID, /* cfgvalue */
+ CDCACM_CONFIGSTRID, /* icfg */
+ USB_CONFIG_ATTR_ONE|SELFPOWERED|REMOTEWAKEUP, /* attr */
+ (CONFIG_USBDEV_MAXPOWER + 1) / 2 /* mxpower */
+};
+#endif
+
+/* Interface association descriptor */
+
+#if defined(CONFIG_CDCACM_COMPOSITE) && defined(CONFIG_COMPOSITE_IAD)
+static const struct usb_iaddesc_s g_iaddesc =
+{
+ USB_SIZEOF_IADDESC, /* len */
+ USB_DESC_TYPE_INTERFACEASSOCIATION, /* type */
+ CONFIG_CDCACM_IFNOBASE, /* firstif */
+ CDCACM_NINTERFACES, /* nifs */
+ USB_CLASS_CDC, /* class */
+ CDC_SUBCLASS_ACM, /* subclass */
+ CDC_PROTO_NONE, /* protocol */
+ 0 /* ifunction */
+};
+#endif
+
+/* Notification interface */
+
+static const struct usb_ifdesc_s g_notifdesc =
+{
+ USB_SIZEOF_IFDESC, /* len */
+ USB_DESC_TYPE_INTERFACE, /* type */
+ CDCACM_NOTIFID, /* ifno */
+ CDCACM_NOTALTIFID, /* alt */
+ 1, /* neps */
+ USB_CLASS_CDC, /* class */
+ CDC_SUBCLASS_ACM, /* subclass */
+ CDC_PROTO_ATM, /* proto */
+#ifdef CONFIG_CDCACM_NOTIFSTR
+ CDCACM_NOTIFSTRID /* iif */
+#else
+ 0 /* iif */
+#endif
+};
+
+/* Header functional descriptor */
+
+static const struct cdc_hdr_funcdesc_s g_funchdr =
+{
+ SIZEOF_HDR_FUNCDESC, /* size */
+ USB_DESC_TYPE_CSINTERFACE, /* type */
+ CDC_DSUBTYPE_HDR, /* subtype */
+ {
+ LSBYTE(CDC_VERSIONNO), /* LS cdc */
+ MSBYTE(CDC_VERSIONNO) /* MS cdc */
+ }
+};
+
+/* ACM functional descriptor */
+
+static const struct cdc_acm_funcdesc_s g_acmfunc =
+{
+ SIZEOF_ACM_FUNCDESC, /* size */
+ USB_DESC_TYPE_CSINTERFACE, /* type */
+ CDC_DSUBTYPE_ACM, /* subtype */
+ 0x06 /* caps */
+};
+
+/* Union functional descriptor */
+
+static const struct cdc_union_funcdesc_s g_unionfunc =
+{
+ SIZEOF_UNION_FUNCDESC(1), /* size */
+ USB_DESC_TYPE_CSINTERFACE, /* type */
+ CDC_DSUBTYPE_UNION, /* subtype */
+ 0, /* master */
+ {1} /* slave[0] */
+};
+
+/* Interrupt IN endpoint descriptor */
+
+static const struct usb_epdesc_s g_epintindesc =
+{
+ USB_SIZEOF_EPDESC, /* len */
+ USB_DESC_TYPE_ENDPOINT, /* type */
+ CDCACM_EPINTIN_ADDR, /* addr */
+ CDCACM_EPINTIN_ATTR, /* attr */
+ {
+ LSBYTE(CONFIG_CDCACM_EPINTIN_FSSIZE), /* maxpacket (full speed) */
+ MSBYTE(CONFIG_CDCACM_EPINTIN_FSSIZE)
+ },
+ 0xff /* interval */
+};
+
+/* Data interface descriptor */
+
+static const struct usb_ifdesc_s g_dataifdesc =
+{
+ USB_SIZEOF_IFDESC, /* len */
+ USB_DESC_TYPE_INTERFACE, /* type */
+ CDCACM_DATAIFID, /* ifno */
+ CDCACM_DATAALTIFID, /* alt */
+ 2, /* neps */
+ USB_CLASS_CDC_DATA, /* class */
+ CDC_DATA_SUBCLASS_NONE, /* subclass */
+ CDC_DATA_PROTO_NONE, /* proto */
+#ifdef CONFIG_CDCACM_DATAIFSTR
+ CDCACM_DATAIFSTRID /* iif */
+#else
+ 0 /* iif */
+#endif
+};
+
+/* Bulk OUT endpoint descriptor */
+
+static const struct usb_epdesc_s g_epbulkoutdesc =
+{
+ USB_SIZEOF_EPDESC, /* len */
+ USB_DESC_TYPE_ENDPOINT, /* type */
+ CDCACM_EPOUTBULK_ADDR, /* addr */
+ CDCACM_EPOUTBULK_ATTR, /* attr */
+ {
+ LSBYTE(CONFIG_CDCACM_EPBULKOUT_FSSIZE), /* maxpacket (full speed) */
+ MSBYTE(CONFIG_CDCACM_EPBULKOUT_FSSIZE)
+ },
+ 1 /* interval */
+};
+
+/* Bulk IN endpoint descriptor */
+
+static const struct usb_epdesc_s g_epbulkindesc =
+{
+ USB_SIZEOF_EPDESC, /* len */
+ USB_DESC_TYPE_ENDPOINT, /* type */
+ CDCACM_EPINBULK_ADDR, /* addr */
+ CDCACM_EPINBULK_ATTR, /* attr */
+ {
+ LSBYTE(CONFIG_CDCACM_EPBULKIN_FSSIZE), /* maxpacket (full speed) */
+ MSBYTE(CONFIG_CDCACM_EPBULKIN_FSSIZE)
+ },
+ 1 /* interval */
+};
+
+/* The components of the the configuration descriptor are maintained as
+ * a collection of separate descriptor structure coordinated by the
+ * following array. These descriptors could have been combined into
+ * one larger "super" configuration descriptor structure. However, I
+ * have concerns about compiler-dependent alignment and packing. Since
+ * the individual structures consist only of byte types, alignment and
+ * packing is not an issue. And since the are concatentated at run time
+ * instead of compile time, there should no issues there either.
+ */
+
+static const struct cfgdecsc_group_s g_cfggroup[CDCACM_CFGGROUP_SIZE] =
+{
+ /* Configuration Descriptor. If the serial device is used in as part
+ * or a composite device, then the configuration descriptor is
+ * provided by the composite device logic.
+ */
+
+#if !defined(CONFIG_CDCACM_COMPOSITE)
+ {
+ USB_SIZEOF_CFGDESC, /* 1. Configuration descriptor */
+ 0,
+ (FAR void *)&g_cfgdesc
+ },
+
+ /* If the serial device is part of a composite device, then it should
+ * begin with an interface association descriptor (IAD) because the
+ * CDC/ACM device consists of more than one interface. The IAD associates
+ * the two CDC/ACM interfaces with the same CDC/ACM device.
+ */
+
+#elif defined(CONFIG_COMPOSITE_IAD)
+ {
+ USB_SIZEOF_IADDESC, /* 1. Interface association descriptor */
+ 0,
+ (FAR void *)&g_iaddesc
+ },
+#endif
+ {
+ USB_SIZEOF_IFDESC, /* 2. Notification interface */
+ 0,
+ (FAR void *)&g_notifdesc
+ },
+ {
+ SIZEOF_HDR_FUNCDESC, /* 3. Header functional descriptor */
+ 0,
+ (FAR void *)&g_funchdr
+ },
+ {
+ SIZEOF_ACM_FUNCDESC, /* 4. ACM functional descriptor */
+ 0,
+ (FAR void *)&g_acmfunc
+ },
+ {
+ SIZEOF_UNION_FUNCDESC(1), /* 5. Union functional descriptor */
+ 0,
+ (FAR void *)&g_unionfunc
+ },
+ {
+ USB_SIZEOF_EPDESC, /* 6. Interrupt IN endpoint descriptor */
+ CONFIG_CDCACM_EPINTIN_HSSIZE,
+ (FAR void *)&g_epintindesc
+ },
+ {
+ USB_SIZEOF_IFDESC, /* 7. Data interface descriptor */
+ 0,
+ (FAR void *)&g_dataifdesc
+ },
+ {
+ USB_SIZEOF_EPDESC, /* 8. Bulk OUT endpoint descriptor */
+ CONFIG_CDCACM_EPBULKOUT_HSSIZE,
+ (FAR void *)&g_epbulkoutdesc
+ },
+ {
+ USB_SIZEOF_EPDESC, /* 9. Bulk OUT endpoint descriptor */
+ CONFIG_CDCACM_EPBULKIN_HSSIZE,
+ (FAR void *)&g_epbulkindesc
+ }
+};
+
+#if !defined(CONFIG_CDCACM_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED)
+static const struct usb_qualdesc_s g_qualdesc =
+{
+ USB_SIZEOF_QUALDESC, /* len */
+ USB_DESC_TYPE_DEVICEQUALIFIER, /* type */
+ { /* usb */
+ LSBYTE(0x0200),
+ MSBYTE(0x0200)
+ },
+ USB_CLASS_VENDOR_SPEC, /* class */
+ 0, /* subclass */
+ 0, /* protocol */
+ CONFIG_CDCACM_EP0MAXPACKET, /* mxpacketsize */
+ CDCACM_NCONFIGS, /* nconfigs */
+ 0, /* reserved */
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cdcacm_mkstrdesc
+ *
+ * Description:
+ * Construct a string descriptor
+ *
+ ****************************************************************************/
+
+int cdcacm_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc)
+{
+#if !defined(CONFIG_CDCACM_COMPOSITE) || defined(CONFIG_CDCACM_NOTIFSTR) || \
+ defined(CONFIG_CDCACM_DATAIFSTR)
+
+ const char *str;
+ int len;
+ int ndata;
+ int i;
+
+ switch (id)
+ {
+#ifndef CONFIG_CDCACM_COMPOSITE
+ case 0:
+ {
+ /* Descriptor 0 is the language id */
+
+ strdesc->len = 4;
+ strdesc->type = USB_DESC_TYPE_STRING;
+ strdesc->data[0] = LSBYTE(CDCACM_STR_LANGUAGE);
+ strdesc->data[1] = MSBYTE(CDCACM_STR_LANGUAGE);
+ return 4;
+ }
+
+ case CDCACM_MANUFACTURERSTRID:
+ str = CONFIG_CDCACM_VENDORSTR;
+ break;
+
+ case CDCACM_PRODUCTSTRID:
+ str = CONFIG_CDCACM_PRODUCTSTR;
+ break;
+
+ case CDCACM_SERIALSTRID:
+ str = CONFIG_CDCACM_SERIALSTR;
+ break;
+
+ case CDCACM_CONFIGSTRID:
+ str = CONFIG_CDCACM_CONFIGSTR;
+ break;
+#endif
+
+#ifdef CONFIG_CDCACM_NOTIFSTR
+ case CDCACM_NOTIFSTRID:
+ str = CONFIG_CDCACM_NOTIFSTR;
+ break;
+#endif
+
+#ifdef CONFIG_CDCACM_DATAIFSTR
+ case CDCACM_DATAIFSTRID:
+ str = CONFIG_CDCACM_DATAIFSTR;
+ break;
+#endif
+
+ default:
+ return -EINVAL;
+ }
+
+ /* The string is utf16-le. The poor man's utf-8 to utf16-le
+ * conversion below will only handle 7-bit en-us ascii
+ */
+
+ len = strlen(str);
+ for (i = 0, ndata = 0; i < len; i++, ndata += 2)
+ {
+ strdesc->data[ndata] = str[i];
+ strdesc->data[ndata+1] = 0;
+ }
+
+ strdesc->len = ndata+2;
+ strdesc->type = USB_DESC_TYPE_STRING;
+ return strdesc->len;
+#else
+ return -EINVAL;
+#endif
+}
+
+/****************************************************************************
+ * Name: cdcacm_getepdesc
+ *
+ * Description:
+ * Return a pointer to the raw device descriptor
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_CDCACM_COMPOSITE
+FAR const struct usb_devdesc_s *cdcacm_getdevdesc(void)
+{
+ return &g_devdesc;
+}
+#endif
+
+/****************************************************************************
+ * Name: cdcacm_getepdesc
+ *
+ * Description:
+ * Return a pointer to the raw endpoint struct (used for configuring
+ * endpoints)
+ *
+ ****************************************************************************/
+
+FAR const struct usb_epdesc_s *cdcacm_getepdesc(enum cdcacm_epdesc_e epid)
+{
+ switch (epid)
+ {
+ case CDCACM_EPINTIN: /* Interrupt IN endpoint */
+ return &g_epintindesc;
+
+ case CDCACM_EPBULKOUT: /* Bulk OUT endpoint */
+ return &g_epbulkoutdesc;
+
+ case CDCACM_EPBULKIN: /* Bulk IN endpoint */
+ return &g_epbulkindesc;
+
+ default:
+ return NULL;
+ }
+}
+
+/****************************************************************************
+ * Name: cdcacm_mkepdesc
+ *
+ * Description:
+ * Construct the endpoint descriptor using the correct max packet size.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+void cdcacm_mkepdesc(num cdcacm_epdesc_e epid, uint16_t mxpacket,
+ FAR struct usb_epdesc_s *outdesc)
+{
+ FAR const struct usb_epdesc_s *indesc;
+
+ /* Copy the "canned" descriptor */
+
+ indesc = cdcacm_getepdesc(epid)
+ memcpy(outdesc, indesc, USB_SIZEOF_EPDESC);
+
+ /* Then add the correct max packet size */
+
+ outdesc->mxpacketsize[0] = LSBYTE(mxpacket);
+ outdesc->mxpacketsize[1] = MSBYTE(mxpacket);
+}
+#endif
+
+/****************************************************************************
+ * Name: cdcacm_mkcfgdesc
+ *
+ * Description:
+ * Construct the configuration descriptor
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+int16_t cdcacm_mkcfgdesc(FAR uint8_t *buf, uint8_t speed, uint8_t type)
+#else
+int16_t cdcacm_mkcfgdesc(FAR uint8_t *buf)
+#endif
+{
+ FAR const struct cfgdecsc_group_s *group;
+ FAR uint8_t *dest = buf;
+ int i;
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ bool hispeed = (speed == USB_SPEED_HIGH);
+
+ /* Check for switches between high and full speed */
+
+ if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG)
+ {
+ hispeed = !hispeed;
+ }
+#endif
+
+ /* Copy all of the descriptors in the group */
+
+ for (i = 0, dest = buf; i < CDCACM_CFGGROUP_SIZE; i++)
+ {
+ group = &g_cfggroup[i];
+
+ /* The "canned" descriptors all have full speed endpoint maxpacket
+ * sizes. If high speed is selected, we will have to change the
+ * endpoint maxpacket size.
+ *
+ * Is there a alternative high speed maxpacket size in the table?
+ * If so, that is sufficient proof that the descriptor that we
+ * just copied is an endpoint descriptor and needs the fixup
+ */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ if (highspeed && group->hsepsize != 0)
+ {
+ cdcacm_mkepdesc(group->desc, group->hsepsize,
+ (FAR struct usb_epdesc_s*)dest);
+ }
+ else
+#endif
+ /* Copy the "canned" descriptor with the full speed max packet
+ * size
+ */
+
+ {
+ memcpy(dest, group->desc, group->descsize);
+ }
+
+ /* Advance to the destination location for the next descriptor */
+
+ dest += group->descsize;
+ }
+
+ return SIZEOF_CDCACM_CFGDESC;
+}
+
+/****************************************************************************
+ * Name: cdcacm_getqualdesc
+ *
+ * Description:
+ * Return a pointer to the raw qual descriptor
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_CDCACM_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED)
+FAR const struct usb_qualdesc_s *cdcacm_getqualdesc(void)
+{
+ return &g_qualdesc;
+}
+#endif
diff --git a/nuttx/drivers/usbdev/composite.c b/nuttx/drivers/usbdev/composite.c
new file mode 100644
index 000000000..4cad8af86
--- /dev/null
+++ b/nuttx/drivers/usbdev/composite.c
@@ -0,0 +1,933 @@
+/****************************************************************************
+ * drivers/usbdev/composite.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbdev.h>
+#include <nuttx/usb/usbdev_trace.h>
+
+#include "composite.h"
+
+#ifdef CONFIG_USBDEV_COMPOSITE
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure describes the internal state of the driver */
+
+struct composite_dev_s
+{
+ FAR struct usbdev_s *usbdev; /* usbdev driver pointer */
+ uint8_t config; /* Configuration number */
+ FAR struct usbdev_req_s *ctrlreq; /* Allocated control request */
+ struct usbdevclass_driver_s *dev1; /* Device 1 class object */
+ struct usbdevclass_driver_s *dev2; /* Device 2 class object */
+};
+
+/* The internal version of the class driver */
+
+struct composite_driver_s
+{
+ struct usbdevclass_driver_s drvr;
+ FAR struct composite_dev_s *dev;
+};
+
+/* This is what is allocated */
+
+struct composite_alloc_s
+{
+ struct composite_dev_s dev;
+ struct composite_driver_s drvr;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* USB helps ****************************************************************/
+
+static void composite_ep0incomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+static int composite_classsetup(FAR struct composite_dev_s *priv,
+ FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout,
+ size_t outlen);
+static struct usbdev_req_s *composite_allocreq(FAR struct usbdev_ep_s *ep,
+ uint16_t len);
+static void composite_freereq(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+
+/* USB class device ********************************************************/
+
+static int composite_bind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+static void composite_unbind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+static int composite_setup(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout,
+ size_t outlen);
+static void composite_disconnect(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+static void composite_suspend(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+static void composite_resume(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+/* USB class device *********************************************************/
+
+static const struct usbdevclass_driverops_s g_driverops =
+{
+ composite_bind, /* bind */
+ composite_unbind, /* unbind */
+ composite_setup, /* setup */
+ composite_disconnect, /* disconnect */
+ composite_suspend, /* suspend */
+ composite_resume, /* resume */
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+const char g_compvendorstr[] = CONFIG_COMPOSITE_VENDORSTR;
+const char g_compproductstr[] = CONFIG_COMPOSITE_PRODUCTSTR;
+const char g_compserialstr[] = CONFIG_COMPOSITE_SERIALSTR;
+
+ /****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Helpers
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: composite_ep0incomplete
+ *
+ * Description:
+ * Handle completion of the composite driver's EP0 control operations
+ *
+ ****************************************************************************/
+
+static void composite_ep0incomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ /* Just check the result of the transfer */
+
+ if (req->result || req->xfrd != req->len)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_REQRESULT), (uint16_t)-req->result);
+ }
+}
+
+/****************************************************************************
+ * Name: composite_classsetup
+ *
+ * Description:
+ * Forward a setup command to the appropriate component device
+ *
+ ****************************************************************************/
+
+static int composite_classsetup(FAR struct composite_dev_s *priv,
+ FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl,
+ FAR uint8_t *dataout, size_t outlen)
+{
+ uint16_t index;
+ uint8_t interface;
+ int ret = -EOPNOTSUPP;
+
+ index = GETUINT16(ctrl->index);
+ interface = (uint8_t)(index & 0xff);
+
+ if (interface >= DEV1_FIRSTINTERFACE && interface < (DEV1_FIRSTINTERFACE + DEV1_NINTERFACES))
+ {
+ ret = CLASS_SETUP(priv->dev1, dev, ctrl, dataout, outlen);
+ }
+ else if (interface >= DEV2_FIRSTINTERFACE && interface < (DEV2_FIRSTINTERFACE + DEV2_NINTERFACES))
+ {
+ ret = CLASS_SETUP(priv->dev2, dev, ctrl, dataout, outlen);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: composite_allocreq
+ *
+ * Description:
+ * Allocate a request instance along with its buffer
+ *
+ ****************************************************************************/
+
+static struct usbdev_req_s *composite_allocreq(FAR struct usbdev_ep_s *ep,
+ uint16_t len)
+{
+ FAR struct usbdev_req_s *req;
+
+ req = EP_ALLOCREQ(ep);
+ if (req != NULL)
+ {
+ req->len = len;
+ req->buf = EP_ALLOCBUFFER(ep, len);
+ if (!req->buf)
+ {
+ EP_FREEREQ(ep, req);
+ req = NULL;
+ }
+ }
+ return req;
+}
+
+/****************************************************************************
+ * Name: composite_freereq
+ *
+ * Description:
+ * Free a request instance along with its buffer
+ *
+ ****************************************************************************/
+
+static void composite_freereq(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ if (ep != NULL && req != NULL)
+ {
+ if (req->buf != NULL)
+ {
+ EP_FREEBUFFER(ep, req->buf);
+ }
+ EP_FREEREQ(ep, req);
+ }
+}
+
+/****************************************************************************
+ * USB Class Driver Methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: composite_bind
+ *
+ * Description:
+ * Invoked when the driver is bound to a USB device driver
+ *
+ ****************************************************************************/
+
+static int composite_bind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct composite_dev_s *priv = ((FAR struct composite_driver_s*)driver)->dev;
+ int ret;
+
+ usbtrace(TRACE_CLASSBIND, 0);
+
+ /* Bind the structures */
+
+ priv->usbdev = dev;
+
+ /* Save the reference to our private data structure in EP0 so that it
+ * can be recovered in ep0 completion events.
+ */
+
+ dev->ep0->priv = priv;
+
+ /* Preallocate one control request */
+
+ priv->ctrlreq = composite_allocreq(dev->ep0, COMPOSITE_CFGDESCSIZE);
+ if (priv->ctrlreq == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_ALLOCCTRLREQ), 0);
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ /* Initialize the pre-allocated control request */
+
+ priv->ctrlreq->callback = composite_ep0incomplete;
+
+ /* Then bind each of the constituent class drivers */
+
+ ret = CLASS_BIND(priv->dev1, dev);
+ if (ret < 0)
+ {
+ goto errout;
+ }
+
+ ret = CLASS_BIND(priv->dev2, dev);
+ if (ret < 0)
+ {
+ goto errout;
+ }
+
+ /* Report if we are selfpowered */
+
+#ifdef CONFIG_USBDEV_SELFPOWERED
+ DEV_SETSELFPOWERED(dev);
+#endif
+
+ /* And pull-up the data line for the soft connect function */
+
+ DEV_CONNECT(dev);
+ return OK;
+
+errout:
+ composite_unbind(driver, dev);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: composite_unbind
+ *
+ * Description:
+ * Invoked when the driver is unbound from a USB device driver
+ *
+ ****************************************************************************/
+
+static void composite_unbind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct composite_dev_s *priv;
+ irqstate_t flags;
+
+ usbtrace(TRACE_CLASSUNBIND, 0);
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev || !dev->ep0)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = ((FAR struct composite_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EP0NOTBOUND), 0);
+ return;
+ }
+#endif
+
+ /* Make sure that we are not already unbound */
+
+ if (priv != NULL)
+ {
+ /* Unbind the constituent class drivers */
+
+ flags = irqsave();
+ CLASS_UNBIND(priv->dev1, dev);
+ CLASS_UNBIND(priv->dev2, dev);
+
+ /* Free the pre-allocated control request */
+
+ priv->config = COMPOSITE_CONFIGIDNONE;
+ if (priv->ctrlreq != NULL)
+ {
+ composite_freereq(dev->ep0, priv->ctrlreq);
+ priv->ctrlreq = NULL;
+ }
+ irqrestore(flags);
+ }
+}
+
+/****************************************************************************
+ * Name: composite_setup
+ *
+ * Description:
+ * Invoked for ep0 control requests. This function probably executes
+ * in the context of an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int composite_setup(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl,
+ FAR uint8_t *dataout, size_t outlen)
+{
+ FAR struct composite_dev_s *priv;
+ FAR struct usbdev_req_s *ctrlreq;
+ uint16_t value;
+ uint16_t index;
+ uint16_t len;
+ bool dispatched = false;
+ int ret = -EOPNOTSUPP;
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev || !dev->ep0 || !ctrl)
+ {
+ usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_SETUPINVALIDARGS), 0);
+ return -EIO;
+ }
+#endif
+
+ /* Extract a reference to private data */
+
+ usbtrace(TRACE_CLASSSETUP, ctrl->req);
+ priv = ((FAR struct composite_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_EP0NOTBOUND2), 0);
+ return -ENODEV;
+ }
+#endif
+ ctrlreq = priv->ctrlreq;
+
+ /* Extract the little-endian 16-bit values to host order */
+
+ value = GETUINT16(ctrl->value);
+ index = GETUINT16(ctrl->index);
+ len = GETUINT16(ctrl->len);
+
+ uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
+ ctrl->type, ctrl->req, value, index, len);
+
+ if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD)
+ {
+ /**********************************************************************
+ * Standard Requests
+ **********************************************************************/
+
+ switch (ctrl->req)
+ {
+ case USB_REQ_GETDESCRIPTOR:
+ {
+ /* The value field specifies the descriptor type in the MS byte and the
+ * descriptor index in the LS byte (order is little endian)
+ */
+
+ switch (ctrl->value[1])
+ {
+ case USB_DESC_TYPE_DEVICE:
+ {
+ ret = USB_SIZEOF_DEVDESC;
+ memcpy(ctrlreq->buf, composite_getdevdesc(), ret);
+ }
+ break;
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ case USB_DESC_TYPE_DEVICEQUALIFIER:
+ {
+ ret = USB_SIZEOF_QUALDESC;
+ memcpy(ctrlreq->buf, composite_getqualdesc(), ret);
+ }
+ break;
+
+ case USB_DESC_TYPE_OTHERSPEEDCONFIG:
+#endif
+
+ case USB_DESC_TYPE_CONFIG:
+ {
+#ifdef CONFIG_USBDEV_DUALSPEED
+ ret = composite_mkcfgdesc(ctrlreq->buf, dev->speed,
+ ctrl->value[1]);
+#else
+ ret = composite_mkcfgdesc(ctrlreq->buf);
+#endif
+ }
+ break;
+
+ case USB_DESC_TYPE_STRING:
+ {
+ /* value == string index. Zero is the language ID. */
+
+ uint8_t strid = ctrl->value[0];
+ FAR struct usb_strdesc_s *buf = (FAR struct usb_strdesc_s *)ctrlreq->buf;
+
+ if (strid <= COMPOSITE_NSTRIDS)
+ {
+ ret = composite_mkstrdesc(strid, buf);
+ }
+#if DEV1_NSTRIDS > 0
+ else if (strid <= DEV1_STRIDBASE + DEV1_NSTRIDS)
+ {
+ ret = DEV1_MKSTRDESC(strid, buf);
+ }
+#endif
+#if DEV2_NSTRIDS > 0
+ else if (strid <= DEV2_STRIDBASE + DEV2_NSTRIDS)
+ {
+ ret = DEV2_MKSTRDESC(strid, buf);
+ }
+#endif
+ }
+ break;
+
+ default:
+ {
+ usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_GETUNKNOWNDESC), value);
+ }
+ break;
+ }
+ }
+ break;
+
+ case USB_REQ_SETCONFIGURATION:
+ {
+ if (ctrl->type == 0)
+ {
+ /* Save the configuration and inform the constituent classes */
+
+ ret = CLASS_SETUP(priv->dev1, dev, ctrl);
+ dispatched = true;
+
+ if (ret >= 0)
+ {
+ ret = CLASS_SETUP(priv->dev2, dev, ctrl);
+ if (ret >= 0)
+ {
+ priv->config = value;
+ }
+ }
+ }
+ }
+ break;
+
+ case USB_REQ_GETCONFIGURATION:
+ {
+ if (ctrl->type == USB_DIR_IN)
+ {
+ ctrlreq->buf[0] = priv->config;
+ ret = 1;
+ }
+ }
+ break;
+
+ case USB_REQ_SETINTERFACE:
+ {
+ if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE &&
+ priv->config == COMPOSITE_CONFIGID)
+ {
+ ret = composite_classsetup(priv, dev, ctrl, dataout, outlen);
+ dispatched = true;
+ }
+ }
+ break;
+
+ case USB_REQ_GETINTERFACE:
+ {
+ if (ctrl->type == (USB_DIR_IN|USB_REQ_RECIPIENT_INTERFACE) &&
+ priv->config == COMPOSITE_CONFIGIDNONE)
+ {
+ ret = composite_classsetup(priv, dev, ctrl, dataout, outlen);
+ dispatched = true;
+ }
+ }
+ break;
+
+ default:
+ usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req);
+ break;
+ }
+ }
+ else
+ {
+ uint8_t recipient;
+
+ /**********************************************************************
+ * Non-Standard Class Requests
+ **********************************************************************/
+
+ /* Class implementations should handle there own interface and endpoint
+ * requests.
+ */
+
+ recipient = ctrl->type & USB_REQ_RECIPIENT_MASK;
+ if (recipient == USB_REQ_RECIPIENT_INTERFACE || recipient == USB_REQ_RECIPIENT_ENDPOINT)
+ {
+ ret = composite_classsetup(priv, dev, ctrl, dataout, outlen);
+ dispatched = true;
+ }
+ }
+
+
+ /* Respond to the setup command if (1) data was returned, and (2) the request was
+ * NOT successfully dispatched to the component class driver. On an error return
+ * value (ret < 0), the USB driver will stall EP0.
+ */
+
+ if (ret >= 0 && !dispatched)
+ {
+ /* Setup the request */
+
+ ctrlreq->len = MIN(len, ret);
+ ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;
+
+ /* And submit the request to the USB controller driver */
+
+ ret = EP_SUBMIT(dev->ep0, ctrlreq);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_EPRESPQ), (uint16_t)-ret);
+ ctrlreq->result = OK;
+ composite_ep0incomplete(dev->ep0, ctrlreq);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: composite_disconnect
+ *
+ * Description:
+ * Invoked after all transfers have been stopped, when the host is
+ * disconnected. This function is probably called from the context of an
+ * interrupt handler.
+ *
+ ****************************************************************************/
+
+static void composite_disconnect(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct composite_dev_s *priv;
+ irqstate_t flags;
+
+ usbtrace(TRACE_CLASSDISCONNECT, 0);
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = ((FAR struct composite_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EP0NOTBOUND), 0);
+ return;
+ }
+#endif
+
+ /* Reset the configuration and inform the constituent class drivers of
+ * the disconnection.
+ */
+
+ flags = irqsave();
+ priv->config = COMPOSITE_CONFIGIDNONE;
+ CLASS_DISCONNECT(priv->dev1, dev);
+ CLASS_DISCONNECT(priv->dev2, dev);
+ irqrestore(flags);
+
+ /* Perform the soft connect function so that we will we can be
+ * re-enumerated.
+ */
+
+ DEV_CONNECT(dev);
+}
+
+/****************************************************************************
+ * Name: composite_suspend
+ *
+ * Description:
+ * Invoked on a USB suspend event.
+ *
+ ****************************************************************************/
+
+static void composite_suspend(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct composite_dev_s *priv;
+ irqstate_t flags;
+
+ usbtrace(TRACE_CLASSSUSPEND, 0);
+
+#ifdef CONFIG_DEBUG
+ if (!dev)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = ((FAR struct composite_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EP0NOTBOUND), 0);
+ return;
+ }
+#endif
+
+ /* Forward the suspend event to the constituent devices */
+
+ flags = irqsave();
+ CLASS_SUSPEND(priv->dev1, priv->usbdev);
+ CLASS_SUSPEND(priv->dev2, priv->usbdev);
+ irqrestore(flags);
+}
+
+/****************************************************************************
+ * Name: composite_resume
+ *
+ * Description:
+ * Invoked on a USB resume event.
+ *
+ ****************************************************************************/
+
+static void composite_resume(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct composite_dev_s *priv = NULL;
+ irqstate_t flags;
+
+#ifdef CONFIG_DEBUG
+ if (!dev)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = ((FAR struct composite_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EP0NOTBOUND), 0);
+ return;
+ }
+#endif
+
+ /* Forward the resume event to the constituent devices */
+
+ flags = irqsave();
+ CLASS_RESUME(priv->dev1, priv->usbdev);
+ CLASS_RESUME(priv->dev2, priv->usbdev);
+ irqrestore(flags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Name: composite_initialize
+ *
+ * Description:
+ * Register USB composite device as configured. This function will call
+ * board-specific implementations in order to obtain the class objects for
+ * each of the members of the composite (see board_mscclassobject(),
+ * board_cdcclassobjec(), ...)
+ *
+ * Input Parameter:
+ * None
+ *
+ * Returned Value:
+ * A non-NULL "handle" is returned on success. This handle may be used
+ * later with composite_uninitialize() in order to removed the composite
+ * device. This handle is the (untyped) internal representation of the
+ * the class driver instance.
+ *
+ * NULL is returned on any failure.
+ *
+ ****************************************************************************/
+
+FAR void *composite_initialize(void)
+{
+ FAR struct composite_alloc_s *alloc;
+ FAR struct composite_dev_s *priv;
+ FAR struct composite_driver_s *drvr;
+ int ret;
+
+ /* Allocate the structures needed */
+
+ alloc = (FAR struct composite_alloc_s*)kmalloc(sizeof(struct composite_alloc_s));
+ if (!alloc)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_ALLOCDEVSTRUCT), 0);
+ return NULL;
+ }
+
+ /* Convenience pointers into the allocated blob */
+
+ priv = &alloc->dev;
+ drvr = &alloc->drvr;
+
+ /* Initialize the USB composite driver structure */
+
+ memset(priv, 0, sizeof(struct composite_dev_s));
+
+ /* Get the constitueat class driver objects */
+
+ ret = DEV1_CLASSOBJECT(&priv->dev1);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_CLASSOBJECT), (uint16_t)-ret);
+ goto errout_with_alloc;
+ }
+
+ ret = DEV2_CLASSOBJECT(&priv->dev2);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_CLASSOBJECT), (uint16_t)-ret);
+ goto errout_with_alloc;
+ }
+
+ /* Initialize the USB class driver structure */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ drvr->drvr.speed = USB_SPEED_HIGH;
+#else
+ drvr->drvr.speed = USB_SPEED_FULL;
+#endif
+ drvr->drvr.ops = &g_driverops;
+ drvr->dev = priv;
+
+ /* Register the USB composite class driver */
+
+ ret = usbdev_register(&drvr->drvr);
+ if (ret)
+ {
+ usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_DEVREGISTER), (uint16_t)-ret);
+ goto errout_with_alloc;
+ }
+
+ return (FAR void *)alloc;
+
+errout_with_alloc:
+ kfree(alloc);
+ return NULL;
+}
+
+/****************************************************************************
+ * Name: composite_uninitialize
+ *
+ * Description:
+ * Un-initialize the USB composite driver. The handle is the USB composite
+ * class' device object as was returned by composite_initialize(). This
+ * function will call board-specific implementations in order to free the
+ * class objects for each of the members of the composite (see
+ * board_mscuninitialize(), board_cdcuninitialize(), ...)
+ *
+ * Input Parameters:
+ * handle - The handle returned by a previous call to composite_initialize().
+ *
+ * Returned Value:
+ * None
+ *
+ ***************************************************************************/
+
+void composite_uninitialize(FAR void *handle)
+{
+ FAR struct composite_alloc_s *alloc = (FAR struct composite_alloc_s *)handle;
+ FAR struct composite_dev_s *priv;
+
+ DEBUGASSERT(alloc != NULL);
+
+ /* Uninitialize each of the member classes */
+
+ priv = &alloc->dev;
+ if (priv->dev1)
+ {
+ DEV1_UNINITIALIZE(priv->dev1);
+ priv->dev1 = NULL;
+ }
+
+ if (priv->dev2)
+ {
+ DEV1_UNINITIALIZE(priv->dev2);
+ priv->dev2 = NULL;
+ }
+
+ /* Then unregister and destroy the composite class */
+
+ usbdev_unregister(&alloc->drvr.drvr);
+
+ /* Free any resources used by the composite driver */
+ /* None */
+
+ /* Then free the composite driver state structure itself */
+
+ kfree(priv);
+}
+
+/****************************************************************************
+ * Name: composite_ep0submit
+ *
+ * Description:
+ * Members of the composite cannot send on EP0 directly because EP0 is
+ * is "owned" by the composite device. Instead, when configured as members
+ * of a composite device, those classes should call this method so that
+ * the composite device can send on EP0 onbehalf of the class.
+ *
+ ****************************************************************************/
+
+int composite_ep0submit(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev,
+ FAR struct usbdev_req_s *ctrlreq)
+{
+ /* This function is not really necessary in the current design. However,
+ * keeping this will provide us a little flexibility in the future if
+ * it becomes necessary to manage the completion callbacks.
+ */
+
+ return EP_SUBMIT(dev->ep0, ctrlreq);
+}
+
+#endif /* CONFIG_USBDEV_COMPOSITE */
diff --git a/nuttx/drivers/usbdev/composite.h b/nuttx/drivers/usbdev/composite.h
new file mode 100644
index 000000000..0c022427c
--- /dev/null
+++ b/nuttx/drivers/usbdev/composite.h
@@ -0,0 +1,326 @@
+/****************************************************************************
+ * drivers/usbdev/composite.h
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_USBDEV_COMPOSITE_H
+#define __DRIVERS_USBDEV_COMPOSITE_H 1
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbdev_trace.h>
+
+#ifdef CONFIG_USBDEV_COMPOSITE
+
+#ifdef CONFIG_CDCACM_COMPOSITE
+# include <nuttx/usb/cdcacm.h>
+# include "cdcacm.h"
+#endif
+
+#ifdef CONFIG_USBMSC_COMPOSITE
+# include "usbmsc.h"
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+/* Packet sizes */
+
+#ifndef CONFIG_COMPOSITE_EP0MAXPACKET
+# define CONFIG_COMPOSITE_EP0MAXPACKET 64
+#endif
+
+/* Vendor and product IDs and strings */
+
+#ifndef CONFIG_COMPOSITE_COMPOSITE
+# ifndef CONFIG_COMPOSITE_VENDORID
+# warning "CONFIG_COMPOSITE_VENDORID not defined"
+# define CONFIG_COMPOSITE_VENDORID 0x03eb
+# endif
+
+# ifndef CONFIG_COMPOSITE_PRODUCTID
+# warning "CONFIG_COMPOSITE_PRODUCTID not defined"
+# define CONFIG_COMPOSITE_PRODUCTID 0x2022
+# endif
+
+# ifndef CONFIG_COMPOSITE_VERSIONNO
+# define CONFIG_COMPOSITE_VERSIONNO (0x0101)
+# endif
+
+# ifndef CONFIG_COMPOSITE_VENDORSTR
+# warning "No Vendor string specified"
+# define CONFIG_COMPOSITE_VENDORSTR "NuttX"
+# endif
+
+# ifndef CONFIG_COMPOSITE_PRODUCTSTR
+# warning "No Product string specified"
+# define CONFIG_COMPOSITE_PRODUCTSTR "Composite Device"
+# endif
+
+# undef CONFIG_COMPOSITE_SERIALSTR
+# define CONFIG_COMPOSITE_SERIALSTR "0101"
+#endif
+
+#undef CONFIG_COMPOSITE_CONFIGSTR
+#define CONFIG_COMPOSITE_CONFIGSTR "Composite"
+
+/* Constituent devices ******************************************************/
+
+#undef DEV1_IS_CDCACM
+#undef DEV1_IS_USBMSC
+
+#undef DEV2_IS_CDCACM
+#undef DEV2_IS_USBMSC
+
+/* Pick the first device in the composite. At present, this may only be
+ * the CDC serial device or the mass storage device.
+ */
+
+#if defined(CONFIG_CDCACM_COMPOSITE)
+# define DEV1_IS_CDCACM 1
+# define DEV1_MKCFGDESC cdcacm_mkcfgdesc
+# define DEV1_MKSTRDESC cdcacm_mkstrdesc
+# define DEV1_CLASSOBJECT board_cdcclassobject
+# define DEV1_UNINITIALIZE board_cdcuninitialize
+# define DEV1_NCONFIGS CDCACM_NCONFIGS
+# define DEV1_CONFIGID CDCACM_CONFIGID
+# define DEV1_FIRSTINTERFACE CONFIG_CDCACM_IFNOBASE
+# define DEV1_NINTERFACES CDCACM_NINTERFACES
+# define DEV1_STRIDBASE CONFIG_CDCACM_STRBASE
+# define DEV1_NSTRIDS CDCACM_NSTRIDS
+# define DEV1_CFGDESCSIZE SIZEOF_CDCACM_CFGDESC
+#elif defined(CONFIG_CDCACM_COMPOSITE)
+# define DEV1_IS_USBMSC 1
+# define DEV1_MKCFGDESC usbmsc_mkcfgdesc
+# define DEV1_MKSTRDESC usbmsc_mkstrdesc
+# define DEV1_CLASSOBJECT board_mscclassobject
+# define DEV1_UNINITIALIZE board_mscuninitialize
+# define DEV1_NCONFIGS USBMSC_NCONFIGS
+# define DEV1_CONFIGID USBMSC_CONFIGID
+# define DEV1_FIRSTINTERFACE CONFIG_USBMSC_IFNOBASE
+# define DEV1_NINTERFACES USBMSC_NINTERFACES
+# define DEV1_STRIDBASE CONFIG_USBMSC_IFNOBASE
+# define DEV1_NSTRIDS USBMSC_NSTRIDS
+# define DEV1_CFGDESCSIZE SIZEOF_USBMSC_CFGDESC
+#else
+# error "No members of the composite defined"
+#endif
+
+/* Pick the second device in the composite. At present, this may only be
+ * the CDC serial device or the mass storage device.
+ */
+
+#if defined(CONFIG_CDCACM_COMPOSITE) && !defined(DEV1_IS_CDCACM)
+# define DEV2_IS_CDCACM 1
+# define DEV2_MKCFGDESC cdcacm_mkcfgdesc
+# define DEV2_MKSTRDESC cdcacm_mkstrdesc
+# define DEV2_CLASSOBJECT board_cdcclassobject
+# define DEV2_UNINITIALIZE board_cdcuninitialize
+# define DEV2_NCONFIGS CDCACM_NCONFIGS
+# define DEV2_CONFIGID CDCACM_CONFIGID
+# define DEV2_FIRSTINTERFACE CONFIG_CDCACM_IFNOBASE
+# define DEV2_NINTERFACES CDCACM_NINTERFACES
+# define DEV2_STRIDBASE CONFIG_CDCACM_STRBASE
+# define DEV2_NSTRIDS CDCACM_NSTRIDS
+# define DEV2_CFGDESCSIZE SIZEOF_CDCACM_CFGDESC
+#elif defined(CONFIG_CDCACM_COMPOSITE) && !defined(DEV1_IS_USBMSC)
+# define DEV2_IS_USBMSC 1
+# define DEV2_MKCFGDESC usbmsc_mkcfgdesc
+# define DEV2_MKSTRDESC usbmsc_mkstrdesc
+# define DEV2_UNINITIALIZE board_mscuninitialize
+# define DEV2_CLASSOBJECT board_mscclassobject
+# define DEV2_NCONFIGS USBMSC_NCONFIGS
+# define DEV2_CONFIGID USBMSC_CONFIGID
+# define DEV2_FIRSTINTERFACE CONFIG_USBMSC_IFNOBASE
+# define DEV2_NINTERFACES USBMSC_NINTERFACES
+# define DEV2_STRIDBASE CONFIG_USBMSC_STRBASE
+# define DEV2_NSTRIDS USBMSC_NSTRIDS
+# define DEV2_CFGDESCSIZE SIZEOF_USBMSC_CFGDESC
+#else
+# error "Insufficient members of the composite defined"
+#endif
+
+/* Verify interface configuration */
+
+#if DEV1_FIRSTINTERFACE != 0
+# warning "The first interface number should be zero"
+#endif
+
+#if (DEV1_FIRSTINTERFACE + DEV1_NINTERFACES) != DEV2_FIRSTINTERFACE
+# warning "Interface numbers are not contiguous"
+#endif
+
+/* Check if an IAD is needed */
+
+#ifdef CONFIG_COMPOSITE_IAD
+# if DEV1_NINTERFACES == 1 && DEV2_NINTERFACES == 1
+# warning "CONFIG_COMPOSITE_IAD not needed"
+# endif
+#endif
+
+#if !defined(CONFIG_COMPOSITE_IAD) && DEV1_NINTERFACES > 1 && DEV2_NINTERFACES > 1
+# warning "CONFIG_COMPOSITE_IAD may be needed"
+#endif
+
+/* Total size of the configuration descriptor: */
+
+#define COMPOSITE_CFGDESCSIZE (USB_SIZEOF_CFGDESC + DEV1_CFGDESCSIZE + DEV2_CFGDESCSIZE)
+
+/* The total number of interfaces */
+
+#define COMPOSITE_NINTERFACES (DEV1_NINTERFACES + DEV2_NINTERFACES)
+
+/* Composite configuration ID value */
+
+#if DEV1_NCONFIGS != 1 || DEV1_CONFIGID != 1
+# error "DEV1: Only a single configuration is supported"
+#endif
+
+#if DEV2_NCONFIGS != 1 || DEV2_CONFIGID != 1
+# error "DEV2: Only a single configuration is supported"
+#endif
+
+/* Descriptors **************************************************************/
+/* These settings are not modifiable via the NuttX configuration */
+
+#define COMPOSITE_CONFIGIDNONE (0) /* Config ID = 0 means to return to address mode */
+#define COMPOSITE_NCONFIGS (1) /* The number of configurations supported */
+#define COMPOSITE_CONFIGID (1) /* The only supported configuration ID */
+
+/* String language */
+
+#define COMPOSITE_STR_LANGUAGE (0x0409) /* en-us */
+
+/* Descriptor strings */
+
+#define COMPOSITE_MANUFACTURERSTRID (1)
+#define COMPOSITE_PRODUCTSTRID (2)
+#define COMPOSITE_SERIALSTRID (3)
+#define COMPOSITE_CONFIGSTRID (4)
+#define COMPOSITE_NSTRIDS (4)
+
+/* Verify string configuration */
+
+#if COMPOSITE_NSTRIDS != DEV1_STRIDBASE
+# warning "The DEV1 string base should be COMPOSITE_NSTRIDS"
+#endif
+
+#if (DEV1_STRIDBASE + DEV1_NSTRIDS) != DEV2_STRIDBASE
+# warning "String IDs are not contiguous"
+#endif
+
+/* Everpresent MIN/MAX macros ***********************************************/
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef MAX
+# define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+extern const char g_compvendorstr[];
+extern const char g_compproductstr[];
+extern const char g_compserialstr[];
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: composite_mkstrdesc
+ *
+ * Description:
+ * Construct a string descriptor
+ *
+ ****************************************************************************/
+
+int composite_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc);
+
+/****************************************************************************
+ * Name: composite_getepdesc
+ *
+ * Description:
+ * Return a pointer to the composite device descriptor
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_COMPOSITE_COMPOSITE
+FAR const struct usb_devdesc_s *composite_getdevdesc(void);
+#endif
+
+/****************************************************************************
+ * Name: composite_mkcfgdesc
+ *
+ * Description:
+ * Construct the composite configuration descriptor
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+int16_t composite_mkcfgdesc(uint8_t *buf, uint8_t speed, uint8_t type);
+#else
+int16_t composite_mkcfgdesc(uint8_t *buf);
+#endif
+
+/****************************************************************************
+ * Name: composite_getqualdesc
+ *
+ * Description:
+ * Return a pointer to the composite qual descriptor
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+FAR const struct usb_qualdesc_s *composite_getqualdesc(void);
+#endif
+
+#endif /* CONFIG_USBDEV_COMPOSITE */
+#endif /* __DRIVERS_USBDEV_COMPOSITE_H */
diff --git a/nuttx/drivers/usbdev/composite_desc.c b/nuttx/drivers/usbdev/composite_desc.c
new file mode 100644
index 000000000..0a0cd4a6a
--- /dev/null
+++ b/nuttx/drivers/usbdev/composite_desc.c
@@ -0,0 +1,291 @@
+/****************************************************************************
+ * drivers/usbdev/composite_desc.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/usb/usbdev_trace.h>
+
+#include "composite.h"
+
+#ifdef CONFIG_USBDEV_COMPOSITE
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+typedef int16_t (*mkcfgdesc)(FAR uint8_t *buf, uint8_t speed, uint8_t type);
+#else
+typedef int16_t (*mkcfgdesc)(FAR uint8_t *buf);
+#endif
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+/* Device Descriptor */
+
+static const struct usb_devdesc_s g_devdesc =
+{
+ USB_SIZEOF_DEVDESC, /* len */
+ USB_DESC_TYPE_DEVICE, /* type */
+ { /* usb */
+ LSBYTE(0x0200),
+ MSBYTE(0x0200)
+ },
+#ifdef CONFIG_COMPOSITE_IAD
+ USB_CLASS_MISC, /* classid */
+ 2, /* subclass */
+ 1, /* protocol */
+#else
+ USB_CLASS_PER_INTERFACE, /* classid */
+ 0, /* subclass */
+ 0, /* protocol */
+#endif
+ CONFIG_COMPOSITE_EP0MAXPACKET, /* maxpacketsize */
+ {
+ LSBYTE(CONFIG_COMPOSITE_VENDORID), /* vendor */
+ MSBYTE(CONFIG_COMPOSITE_VENDORID)
+ },
+ {
+ LSBYTE(CONFIG_COMPOSITE_PRODUCTID), /* product */
+ MSBYTE(CONFIG_COMPOSITE_PRODUCTID)
+ },
+ {
+ LSBYTE(CONFIG_COMPOSITE_VERSIONNO), /* device */
+ MSBYTE(CONFIG_COMPOSITE_VERSIONNO)
+ },
+ COMPOSITE_MANUFACTURERSTRID, /* imfgr */
+ COMPOSITE_PRODUCTSTRID, /* iproduct */
+ COMPOSITE_SERIALSTRID, /* serno */
+ COMPOSITE_NCONFIGS /* nconfigs */
+};
+
+/* Configuration descriptor for the composite device */
+
+static const struct usb_cfgdesc_s g_cfgdesc =
+{
+ USB_SIZEOF_CFGDESC, /* len */
+ USB_DESC_TYPE_CONFIG, /* type */
+ {
+ LSBYTE(COMPOSITE_CFGDESCSIZE), /* LS totallen */
+ MSBYTE(COMPOSITE_CFGDESCSIZE) /* MS totallen */
+ },
+ COMPOSITE_NINTERFACES, /* ninterfaces */
+ COMPOSITE_CONFIGID, /* cfgvalue */
+ COMPOSITE_CONFIGSTRID, /* icfg */
+ USB_CONFIG_ATTR_ONE|SELFPOWERED|REMOTEWAKEUP, /* attr */
+ (CONFIG_USBDEV_MAXPOWER + 1) / 2 /* mxpower */
+};
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+static const struct usb_qualdesc_s g_qualdesc =
+{
+ USB_SIZEOF_QUALDESC, /* len */
+ USB_DESC_TYPE_DEVICEQUALIFIER, /* type */
+ { /* usb */
+ LSBYTE(0x0200),
+ MSBYTE(0x0200)
+ },
+ USB_CLASS_VENDOR_SPEC, /* classid */
+ 0, /* subclass */
+ 0, /* protocol */
+ CONFIG_COMPOSITE_EP0MAXPACKET, /* mxpacketsize */
+ COMPOSITE_NCONFIGS, /* nconfigs */
+ 0, /* reserved */
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: composite_mkstrdesc
+ *
+ * Description:
+ * Construct a string descriptor
+ *
+ ****************************************************************************/
+
+int composite_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc)
+{
+ const char *str;
+ int len;
+ int ndata;
+ int i;
+
+ switch (id)
+ {
+ case 0:
+ {
+ /* Descriptor 0 is the language id */
+
+ strdesc->len = 4;
+ strdesc->type = USB_DESC_TYPE_STRING;
+ strdesc->data[0] = LSBYTE(COMPOSITE_STR_LANGUAGE);
+ strdesc->data[1] = MSBYTE(COMPOSITE_STR_LANGUAGE);
+ return 4;
+ }
+
+ case COMPOSITE_MANUFACTURERSTRID:
+ str = g_compvendorstr;
+ break;
+
+ case COMPOSITE_PRODUCTSTRID:
+ str = g_compproductstr;
+ break;
+
+ case COMPOSITE_SERIALSTRID:
+ str = g_compserialstr;
+ break;
+
+ case COMPOSITE_CONFIGSTRID:
+ str = CONFIG_COMPOSITE_CONFIGSTR;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* The string is utf16-le. The poor man's utf-8 to utf16-le
+ * conversion below will only handle 7-bit en-us ascii
+ */
+
+ len = strlen(str);
+ for (i = 0, ndata = 0; i < len; i++, ndata += 2)
+ {
+ strdesc->data[ndata] = str[i];
+ strdesc->data[ndata+1] = 0;
+ }
+
+ strdesc->len = ndata+2;
+ strdesc->type = USB_DESC_TYPE_STRING;
+ return strdesc->len;
+}
+
+/****************************************************************************
+ * Name: composite_getepdesc
+ *
+ * Description:
+ * Return a pointer to the raw device descriptor
+ *
+ ****************************************************************************/
+
+FAR const struct usb_devdesc_s *composite_getdevdesc(void)
+{
+ return &g_devdesc;
+}
+
+/****************************************************************************
+ * Name: composite_mkcfgdesc
+ *
+ * Description:
+ * Construct the configuration descriptor
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+int16_t composite_mkcfgdesc(uint8_t *buf, uint8_t speed, uint8_t type)
+#else
+int16_t composite_mkcfgdesc(uint8_t *buf)
+#endif
+{
+ int16_t len;
+ int16_t total;
+
+ /* Configuration descriptor -- Copy the canned configuration descriptor. */
+
+ memcpy(buf, &g_cfgdesc, USB_SIZEOF_CFGDESC);
+ total = USB_SIZEOF_CFGDESC;
+ buf += USB_SIZEOF_CFGDESC;
+
+ /* Copy DEV1/DEV2 interface descriptors */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ len = DEV1_MKCFGDESC(buf, speed, type);
+ total += len;
+ buf += len;
+ total += DEV2_MKCFGDESC(buf, speed, type);
+#else
+ len = DEV1_MKCFGDESC(buf);
+ total += len;
+ buf += len;
+ total += DEV2_MKCFGDESC(buf);
+#endif
+
+ DEBUGASSERT(total == COMPOSITE_CFGDESCSIZE);
+ return total;
+}
+
+/****************************************************************************
+ * Name: composite_getqualdesc
+ *
+ * Description:
+ * Return a pointer to the raw qual descriptor
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+FAR const struct usb_qualdesc_s *composite_getqualdesc(void)
+{
+ return &g_qualdesc;
+}
+#endif
+
+#endif /* CONFIG_USBDEV_COMPOSITE */
diff --git a/nuttx/drivers/usbdev/pl2303.c b/nuttx/drivers/usbdev/pl2303.c
new file mode 100644
index 000000000..69bf87965
--- /dev/null
+++ b/nuttx/drivers/usbdev/pl2303.c
@@ -0,0 +1,2242 @@
+/****************************************************************************
+ * drivers/usbdev/pl2303.c
+ *
+ * Copyright (C) 2008-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * This logic emulates the Prolific PL2303 serial/USB converter
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <string.h>
+#include <errno.h>
+#include <queue.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/arch.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbdev.h>
+#include <nuttx/usb/usbdev_trace.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* Number of requests in the write queue */
+
+#ifndef CONFIG_PL2303_NWRREQS
+# define CONFIG_PL2303_NWRREQS 4
+#endif
+
+/* Number of requests in the read queue */
+
+#ifndef CONFIG_PL2303_NRDREQS
+# define CONFIG_PL2303_NRDREQS 4
+#endif
+
+/* Logical endpoint numbers / max packet sizes */
+
+#ifndef CONFIG_PL2303_EPINTIN
+# warning "EPINTIN not defined in the configuration"
+# define CONFIG_PL2303_EPINTIN 1
+#endif
+
+#ifndef CONFIG_PL2303_EPBULKOUT
+# warning "EPBULKOUT not defined in the configuration"
+# define CONFIG_PL2303_EPBULKOUT 2
+#endif
+
+#ifndef CONFIG_PL2303_EPBULKIN
+# warning "EPBULKIN not defined in the configuration"
+# define CONFIG_PL2303_EPBULKIN 3
+#endif
+
+/* Packet and request buffer sizes */
+
+#ifndef CONFIG_PL2303_EP0MAXPACKET
+# define CONFIG_PL2303_EP0MAXPACKET 64
+#endif
+
+#undef CONFIG_PL2303_BULKREQLEN
+
+/* Vendor and product IDs and strings */
+
+#ifndef CONFIG_PL2303_VENDORID
+# define CONFIG_PL2303_VENDORID 0x067b
+#endif
+
+#ifndef CONFIG_PL2303_PRODUCTID
+# define CONFIG_PL2303_PRODUCTID 0x2303
+#endif
+
+#ifndef CONFIG_PL2303_VENDORSTR
+# warning "No Vendor string specified"
+# define CONFIG_PL2303_VENDORSTR "NuttX"
+#endif
+
+#ifndef CONFIG_PL2303_PRODUCTSTR
+# warning "No Product string specified"
+# define CONFIG_PL2303_PRODUCTSTR "USBdev Serial"
+#endif
+
+#undef CONFIG_PL2303_SERIALSTR
+#define CONFIG_PL2303_SERIALSTR "0"
+
+#undef CONFIG_PL2303_CONFIGSTR
+#define CONFIG_PL2303_CONFIGSTR "Bulk"
+
+/* USB Controller */
+
+#ifndef CONFIG_USBDEV_SELFPOWERED
+# define SELFPOWERED USB_CONFIG_ATT_SELFPOWER
+#else
+# define SELFPOWERED (0)
+#endif
+
+#ifndef CONFIG_USBDEV_REMOTEWAKEUP
+# define REMOTEWAKEUP USB_CONFIG_ATTR_WAKEUP
+#else
+# define REMOTEWAKEUP (0)
+#endif
+
+#ifndef CONFIG_USBDEV_MAXPOWER
+# define CONFIG_USBDEV_MAXPOWER 100
+#endif
+
+/* Descriptors ****************************************************************/
+
+/* These settings are not modifiable via the NuttX configuration */
+
+#define PL2303_VERSIONNO (0x0202) /* Device version number */
+#define PL2303_CONFIGIDNONE (0) /* Config ID means to return to address mode */
+#define PL2303_CONFIGID (1) /* The only supported configuration ID */
+#define PL2303_NCONFIGS (1) /* Number of configurations supported */
+#define PL2303_INTERFACEID (0)
+#define PL2303_ALTINTERFACEID (0)
+#define PL2303_NINTERFACES (1) /* Number of interfaces in the configuration */
+#define PL2303_NENDPOINTS (3) /* Number of endpoints in the interface */
+
+/* Endpoint configuration */
+
+#define PL2303_EPINTIN_ADDR (USB_DIR_IN|CONFIG_PL2303_EPINTIN)
+#define PL2303_EPINTIN_ATTR (USB_EP_ATTR_XFER_INT)
+#define PL2303_EPINTIN_MXPACKET (10)
+
+#define PL2303_EPOUTBULK_ADDR (CONFIG_PL2303_EPBULKOUT)
+#define PL2303_EPOUTBULK_ATTR (USB_EP_ATTR_XFER_BULK)
+
+#define PL2303_EPINBULK_ADDR (USB_DIR_IN|CONFIG_PL2303_EPBULKIN)
+#define PL2303_EPINBULK_ATTR (USB_EP_ATTR_XFER_BULK)
+
+/* String language */
+
+#define PL2303_STR_LANGUAGE (0x0409) /* en-us */
+
+/* Descriptor strings */
+
+#define PL2303_MANUFACTURERSTRID (1)
+#define PL2303_PRODUCTSTRID (2)
+#define PL2303_SERIALSTRID (3)
+#define PL2303_CONFIGSTRID (4)
+
+/* Buffer big enough for any of our descriptors */
+
+#define PL2303_MXDESCLEN (64)
+
+/* Vender specific control requests *******************************************/
+
+#define PL2303_CONTROL_TYPE (0x20)
+#define PL2303_SETLINEREQUEST (0x20) /* OUT, Recipient interface */
+#define PL2303_GETLINEREQUEST (0x21) /* IN, Recipient interface */
+#define PL2303_SETCONTROLREQUEST (0x22) /* OUT, Recipient interface */
+#define PL2303_BREAKREQUEST (0x23) /* OUT, Recipient interface */
+
+/* Vendor read/write */
+
+#define PL2303_RWREQUEST_TYPE (0x40)
+#define PL2303_RWREQUEST (0x01) /* IN/OUT, Recipient device */
+
+/* Misc Macros ****************************************************************/
+
+/* min/max macros */
+
+#ifndef min
+# define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef max
+# define max(a,b) ((a)>(b)?(a):(b))
+#endif
+
+/* Trace values *************************************************************/
+
+#define PL2303_CLASSAPI_SETUP TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_SETUP)
+#define PL2303_CLASSAPI_SHUTDOWN TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_SHUTDOWN)
+#define PL2303_CLASSAPI_ATTACH TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_ATTACH)
+#define PL2303_CLASSAPI_DETACH TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_DETACH)
+#define PL2303_CLASSAPI_IOCTL TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_IOCTL)
+#define PL2303_CLASSAPI_RECEIVE TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_RECEIVE)
+#define PL2303_CLASSAPI_RXINT TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_RXINT)
+#define PL2303_CLASSAPI_RXAVAILABLE TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_RXAVAILABLE)
+#define PL2303_CLASSAPI_SEND TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_SEND)
+#define PL2303_CLASSAPI_TXINT TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_TXINT)
+#define PL2303_CLASSAPI_TXREADY TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_TXREADY)
+#define PL2303_CLASSAPI_TXEMPTY TRACE_EVENT(TRACE_CLASSAPI_ID, USBSER_TRACECLASSAPI_TXEMPTY)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Container to support a list of requests */
+
+struct pl2303_req_s
+{
+ FAR struct pl2303_req_s *flink; /* Implements a singly linked list */
+ FAR struct usbdev_req_s *req; /* The contained request */
+};
+
+/* This structure describes the internal state of the driver */
+
+struct pl2303_dev_s
+{
+ FAR struct uart_dev_s serdev; /* Serial device structure */
+ FAR struct usbdev_s *usbdev; /* usbdev driver pointer */
+
+ uint8_t config; /* Configuration number */
+ uint8_t nwrq; /* Number of queue write requests (in reqlist)*/
+ uint8_t nrdq; /* Number of queue read requests (in epbulkout) */
+ bool rxenabled; /* true: UART RX "interrupts" enabled */
+ uint8_t linest[7]; /* Fake line status */
+ int16_t rxhead; /* Working head; used when rx int disabled */
+
+ FAR struct usbdev_ep_s *epintin; /* Interrupt IN endpoint structure */
+ FAR struct usbdev_ep_s *epbulkin; /* Bulk IN endpoint structure */
+ FAR struct usbdev_ep_s *epbulkout; /* Bulk OUT endpoint structure */
+ FAR struct usbdev_req_s *ctrlreq; /* Control request */
+ struct sq_queue_s reqlist; /* List of write request containers */
+
+ /* Pre-allocated write request containers. The write requests will
+ * be linked in a free list (reqlist), and used to send requests to
+ * EPBULKIN; Read requests will be queued in the EBULKOUT.
+ */
+
+ struct pl2303_req_s wrreqs[CONFIG_PL2303_NWRREQS];
+ struct pl2303_req_s rdreqs[CONFIG_PL2303_NWRREQS];
+
+ /* Serial I/O buffers */
+
+ char rxbuffer[CONFIG_PL2303_RXBUFSIZE];
+ char txbuffer[CONFIG_PL2303_TXBUFSIZE];
+};
+
+/* The internal version of the class driver */
+
+struct pl2303_driver_s
+{
+ struct usbdevclass_driver_s drvr;
+ FAR struct pl2303_dev_s *dev;
+};
+
+/* This is what is allocated */
+
+struct pl2303_alloc_s
+{
+ struct pl2303_dev_s dev;
+ struct pl2303_driver_s drvr;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Transfer helpers *********************************************************/
+
+static uint16_t usbclass_fillrequest(FAR struct pl2303_dev_s *priv,
+ uint8_t *reqbuf, uint16_t reqlen);
+static int usbclass_sndpacket(FAR struct pl2303_dev_s *priv);
+static inline int usbclass_recvpacket(FAR struct pl2303_dev_s *priv,
+ uint8_t *reqbuf, uint16_t reqlen);
+
+/* Request helpers *********************************************************/
+
+static struct usbdev_req_s *usbclass_allocreq(FAR struct usbdev_ep_s *ep,
+ uint16_t len);
+static void usbclass_freereq(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+
+/* Configuration ***********************************************************/
+
+static int usbclass_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc);
+#ifdef CONFIG_USBDEV_DUALSPEED
+static void usbclass_mkepbulkdesc(const struct usb_epdesc_s *indesc,
+ uint16_t mxpacket, struct usb_epdesc_s *outdesc);
+static int16_t usbclass_mkcfgdesc(uint8_t *buf, uint8_t speed, uint8_t type);
+#else
+static int16_t usbclass_mkcfgdesc(uint8_t *buf);
+#endif
+static void usbclass_resetconfig(FAR struct pl2303_dev_s *priv);
+static int usbclass_setconfig(FAR struct pl2303_dev_s *priv,
+ uint8_t config);
+
+/* Completion event handlers ***********************************************/
+
+static void usbclass_ep0incomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+static void usbclass_wrcomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+
+/* USB class device ********************************************************/
+
+static int usbclass_bind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+static void usbclass_unbind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+static int usbclass_setup(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout,
+ size_t outlen);
+static void usbclass_disconnect(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+
+/* Serial port *************************************************************/
+
+static int usbser_setup(FAR struct uart_dev_s *dev);
+static void usbser_shutdown(FAR struct uart_dev_s *dev);
+static int usbser_attach(FAR struct uart_dev_s *dev);
+static void usbser_detach(FAR struct uart_dev_s *dev);
+static void usbser_rxint(FAR struct uart_dev_s *dev, bool enable);
+static void usbser_txint(FAR struct uart_dev_s *dev, bool enable);
+static bool usbser_txempty(FAR struct uart_dev_s *dev);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/* USB class device ********************************************************/
+
+static const struct usbdevclass_driverops_s g_driverops =
+{
+ usbclass_bind, /* bind */
+ usbclass_unbind, /* unbind */
+ usbclass_setup, /* setup */
+ usbclass_disconnect, /* disconnect */
+ NULL, /* suspend */
+ NULL, /* resume */
+};
+
+/* Serial port *************************************************************/
+
+static const struct uart_ops_s g_uartops =
+{
+ usbser_setup, /* setup */
+ usbser_shutdown, /* shutdown */
+ usbser_attach, /* attach */
+ usbser_detach, /* detach */
+ NULL, /* ioctl */
+ NULL, /* receive */
+ usbser_rxint, /* rxinit */
+ NULL, /* rxavailable */
+ NULL, /* send */
+ usbser_txint, /* txinit */
+ NULL, /* txready */
+ usbser_txempty /* txempty */
+};
+
+/* USB descriptor templates these will be copied and modified **************/
+
+static const struct usb_devdesc_s g_devdesc =
+{
+ USB_SIZEOF_DEVDESC, /* len */
+ USB_DESC_TYPE_DEVICE, /* type */
+ {LSBYTE(0x0200), MSBYTE(0x0200)}, /* usb */
+ USB_CLASS_PER_INTERFACE, /* classid */
+ 0, /* subclass */
+ 0, /* protocol */
+ CONFIG_PL2303_EP0MAXPACKET, /* maxpacketsize */
+ { LSBYTE(CONFIG_PL2303_VENDORID), /* vendor */
+ MSBYTE(CONFIG_PL2303_VENDORID) },
+ { LSBYTE(CONFIG_PL2303_PRODUCTID), /* product */
+ MSBYTE(CONFIG_PL2303_PRODUCTID) },
+ { LSBYTE(PL2303_VERSIONNO), /* device */
+ MSBYTE(PL2303_VERSIONNO) },
+ PL2303_MANUFACTURERSTRID, /* imfgr */
+ PL2303_PRODUCTSTRID, /* iproduct */
+ PL2303_SERIALSTRID, /* serno */
+ PL2303_NCONFIGS /* nconfigs */
+};
+
+static const struct usb_cfgdesc_s g_cfgdesc =
+{
+ USB_SIZEOF_CFGDESC, /* len */
+ USB_DESC_TYPE_CONFIG, /* type */
+ {0, 0}, /* totallen -- to be provided */
+ PL2303_NINTERFACES, /* ninterfaces */
+ PL2303_CONFIGID, /* cfgvalue */
+ PL2303_CONFIGSTRID, /* icfg */
+ USB_CONFIG_ATTR_ONE|SELFPOWERED|REMOTEWAKEUP, /* attr */
+ (CONFIG_USBDEV_MAXPOWER + 1) / 2 /* mxpower */
+};
+
+static const struct usb_ifdesc_s g_ifdesc =
+{
+ USB_SIZEOF_IFDESC, /* len */
+ USB_DESC_TYPE_INTERFACE, /* type */
+ 0, /* ifno */
+ 0, /* alt */
+ PL2303_NENDPOINTS, /* neps */
+ USB_CLASS_VENDOR_SPEC, /* classid */
+ 0, /* subclass */
+ 0, /* protocol */
+ PL2303_CONFIGSTRID /* iif */
+};
+
+static const struct usb_epdesc_s g_epintindesc =
+{
+ USB_SIZEOF_EPDESC, /* len */
+ USB_DESC_TYPE_ENDPOINT, /* type */
+ PL2303_EPINTIN_ADDR, /* addr */
+ PL2303_EPINTIN_ATTR, /* attr */
+ { LSBYTE(PL2303_EPINTIN_MXPACKET), /* maxpacket */
+ MSBYTE(PL2303_EPINTIN_MXPACKET) },
+ 1 /* interval */
+};
+
+static const struct usb_epdesc_s g_epbulkoutdesc =
+{
+ USB_SIZEOF_EPDESC, /* len */
+ USB_DESC_TYPE_ENDPOINT, /* type */
+ PL2303_EPOUTBULK_ADDR, /* addr */
+ PL2303_EPOUTBULK_ATTR, /* attr */
+ { LSBYTE(64), MSBYTE(64) }, /* maxpacket -- might change to 512*/
+ 0 /* interval */
+};
+
+static const struct usb_epdesc_s g_epbulkindesc =
+{
+ USB_SIZEOF_EPDESC, /* len */
+ USB_DESC_TYPE_ENDPOINT, /* type */
+ PL2303_EPINBULK_ADDR, /* addr */
+ PL2303_EPINBULK_ATTR, /* attr */
+ { LSBYTE(64), MSBYTE(64) }, /* maxpacket -- might change to 512*/
+ 0 /* interval */
+};
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+static const struct usb_qualdesc_s g_qualdesc =
+{
+ USB_SIZEOF_QUALDESC, /* len */
+ USB_DESC_TYPE_DEVICEQUALIFIER, /* type */
+ {LSBYTE(0x0200), MSBYTE(0x0200) }, /* USB */
+ USB_CLASS_VENDOR_SPEC, /* classid */
+ 0, /* subclass */
+ 0, /* protocol */
+ CONFIG_PL2303_EP0MAXPACKET, /* mxpacketsize */
+ PL2303_NCONFIGS, /* nconfigs */
+ 0, /* reserved */
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/************************************************************************************
+ * Name: usbclass_fillrequest
+ *
+ * Description:
+ * If there is data to send it is copied to the given buffer. Called either
+ * to initiate the first write operation, or from the completion interrupt handler
+ * service consecutive write operations.
+ *
+ * NOTE: The USB serial driver does not use the serial drivers uart_xmitchars()
+ * API. That logic is essentially duplicated here because unlike UART hardware,
+ * we need to be able to handle writes not byte-by-byte, but packet-by-packet.
+ * Unfortunately, that decision also exposes some internals of the serial driver
+ * in the following.
+ *
+ ************************************************************************************/
+
+static uint16_t usbclass_fillrequest(FAR struct pl2303_dev_s *priv, uint8_t *reqbuf,
+ uint16_t reqlen)
+{
+ FAR uart_dev_t *serdev = &priv->serdev;
+ FAR struct uart_buffer_s *xmit = &serdev->xmit;
+ irqstate_t flags;
+ uint16_t nbytes = 0;
+
+ /* Disable interrupts */
+
+ flags = irqsave();
+
+ /* Transfer bytes while we have bytes available and there is room in the request */
+
+ while (xmit->head != xmit->tail && nbytes < reqlen)
+ {
+ *reqbuf++ = xmit->buffer[xmit->tail];
+ nbytes++;
+
+ /* Increment the tail pointer */
+
+ if (++(xmit->tail) >= xmit->size)
+ {
+ xmit->tail = 0;
+ }
+ }
+
+ /* When all of the characters have been sent from the buffer
+ * disable the "TX interrupt".
+ */
+
+ if (xmit->head == xmit->tail)
+ {
+ uart_disabletxint(serdev);
+ }
+
+ /* If any bytes were removed from the buffer, inform any waiters
+ * there there is space available.
+ */
+
+ if (nbytes)
+ {
+ uart_datasent(serdev);
+ }
+
+ irqrestore(flags);
+ return nbytes;
+}
+
+/************************************************************************************
+ * Name: usbclass_sndpacket
+ *
+ * Description:
+ * This function obtains write requests, transfers the TX data into the request,
+ * and submits the requests to the USB controller. This continues untils either
+ * (1) there are no further packets available, or (2) thre is not further data
+ * to send.
+ *
+ ************************************************************************************/
+
+static int usbclass_sndpacket(FAR struct pl2303_dev_s *priv)
+{
+ FAR struct usbdev_ep_s *ep;
+ FAR struct usbdev_req_s *req;
+ FAR struct pl2303_req_s *reqcontainer;
+ uint16_t reqlen;
+ irqstate_t flags;
+ int len;
+ int ret = OK;
+
+#ifdef CONFIG_DEBUG
+ if (priv == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return -ENODEV;
+ }
+#endif
+
+ flags = irqsave();
+
+ /* Use our IN endpoint for the transfer */
+
+ ep = priv->epbulkin;
+
+ /* Loop until either (1) we run out or write requests, or (2) usbclass_fillrequest()
+ * is unable to fill the request with data (i.e., until there is no more data
+ * to be sent).
+ */
+
+ uvdbg("head=%d tail=%d nwrq=%d empty=%d\n",
+ priv->serdev.xmit.head, priv->serdev.xmit.tail,
+ priv->nwrq, sq_empty(&priv->reqlist));
+
+ /* Get the maximum number of bytes that will fit into one bulk IN request */
+
+#ifdef CONFIG_PL2303_BULKREQLEN
+ reqlen = MAX(CONFIG_PL2303_BULKREQLEN, ep->maxpacket);
+#else
+ reqlen = ep->maxpacket;
+#endif
+
+ while (!sq_empty(&priv->reqlist))
+ {
+ /* Peek at the request in the container at the head of the list */
+
+ reqcontainer = (struct pl2303_req_s *)sq_peek(&priv->reqlist);
+ req = reqcontainer->req;
+
+ /* Fill the request with serial TX data */
+
+ len = usbclass_fillrequest(priv, req->buf, reqlen);
+ if (len > 0)
+ {
+ /* Remove the empty container from the request list */
+
+ (void)sq_remfirst(&priv->reqlist);
+ priv->nwrq--;
+
+ /* Then submit the request to the endpoint */
+
+ req->len = len;
+ req->priv = reqcontainer;
+ req->flags = USBDEV_REQFLAGS_NULLPKT;
+ ret = EP_SUBMIT(ep, req);
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SUBMITFAIL), (uint16_t)-ret);
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ irqrestore(flags);
+ return ret;
+}
+
+/************************************************************************************
+ * Name: usbclass_recvpacket
+ *
+ * Description:
+ * A normal completion event was received by the read completion handler at the
+ * interrupt level (with interrupts disabled). This function handles the USB packet
+ * and provides the received data to the uart RX buffer.
+ *
+ * Assumptions:
+ * Called from the USB interrupt handler with interrupts disabled.
+ *
+ ************************************************************************************/
+
+static inline int usbclass_recvpacket(FAR struct pl2303_dev_s *priv,
+ uint8_t *reqbuf, uint16_t reqlen)
+{
+ FAR uart_dev_t *serdev = &priv->serdev;
+ FAR struct uart_buffer_s *recv = &serdev->recv;
+ uint16_t currhead;
+ uint16_t nexthead;
+ uint16_t nbytes = 0;
+
+ /* Get the next head index. During the time that RX interrupts are disabled, the
+ * the serial driver will be extracting data from the circular buffer and modifying
+ * recv.tail. During this time, we should avoid modifying recv.head; Instead we will
+ * use a shadow copy of the index. When interrupts are restored, the real recv.head
+ * will be updated with this indes.
+ */
+
+ if (priv->rxenabled)
+ {
+ currhead = recv->head;
+ }
+ else
+ {
+ currhead = priv->rxhead;
+ }
+
+ /* Pre-calculate the head index and check for wrap around. We need to do this
+ * so that we can determine if the circular buffer will overrun BEFORE we
+ * overrun the buffer!
+ */
+
+ nexthead = currhead + 1;
+ if (nexthead >= recv->size)
+ {
+ nexthead = 0;
+ }
+
+ /* Then copy data into the RX buffer until either: (1) all of the data has been
+ * copied, or (2) the RX buffer is full. NOTE: If the RX buffer becomes full,
+ * then we have overrun the serial driver and data will be lost.
+ */
+
+ while (nexthead != recv->tail && nbytes < reqlen)
+ {
+ /* Copy one byte to the head of the circular RX buffer */
+
+ recv->buffer[currhead] = *reqbuf++;
+
+ /* Update counts and indices */
+
+ currhead = nexthead;
+ nbytes++;
+
+ /* Increment the head index and check for wrap around */
+
+ nexthead = currhead + 1;
+ if (nexthead >= recv->size)
+ {
+ nexthead = 0;
+ }
+ }
+
+ /* Write back the head pointer using the shadow index if RX "interrupts"
+ * are disabled.
+ */
+
+ if (priv->rxenabled)
+ {
+ recv->head = currhead;
+ }
+ else
+ {
+ priv->rxhead = currhead;
+ }
+
+ /* If data was added to the incoming serial buffer, then wake up any
+ * threads is waiting for incoming data. If we are running in an interrupt
+ * handler, then the serial driver will not run until the interrupt handler
+ * returns.
+ */
+
+ if (priv->rxenabled && nbytes > 0)
+ {
+ uart_datareceived(serdev);
+ }
+
+ /* Return an error if the entire packet could not be transferred */
+
+ if (nbytes < reqlen)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RXOVERRUN), 0);
+ return -ENOSPC;
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbclass_allocreq
+ *
+ * Description:
+ * Allocate a request instance along with its buffer
+ *
+ ****************************************************************************/
+
+static struct usbdev_req_s *usbclass_allocreq(FAR struct usbdev_ep_s *ep,
+ uint16_t len)
+{
+ FAR struct usbdev_req_s *req;
+
+ req = EP_ALLOCREQ(ep);
+ if (req != NULL)
+ {
+ req->len = len;
+ req->buf = EP_ALLOCBUFFER(ep, len);
+ if (!req->buf)
+ {
+ EP_FREEREQ(ep, req);
+ req = NULL;
+ }
+ }
+ return req;
+}
+
+/****************************************************************************
+ * Name: usbclass_freereq
+ *
+ * Description:
+ * Free a request instance along with its buffer
+ *
+ ****************************************************************************/
+
+static void usbclass_freereq(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ if (ep != NULL && req != NULL)
+ {
+ if (req->buf != NULL)
+ {
+ EP_FREEBUFFER(ep, req->buf);
+ }
+ EP_FREEREQ(ep, req);
+ }
+}
+
+/****************************************************************************
+ * Name: usbclass_mkstrdesc
+ *
+ * Description:
+ * Construct a string descriptor
+ *
+ ****************************************************************************/
+
+static int usbclass_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc)
+{
+ const char *str;
+ int len;
+ int ndata;
+ int i;
+
+ switch (id)
+ {
+ case 0:
+ {
+ /* Descriptor 0 is the language id */
+
+ strdesc->len = 4;
+ strdesc->type = USB_DESC_TYPE_STRING;
+ strdesc->data[0] = LSBYTE(PL2303_STR_LANGUAGE);
+ strdesc->data[1] = MSBYTE(PL2303_STR_LANGUAGE);
+ return 4;
+ }
+
+ case PL2303_MANUFACTURERSTRID:
+ str = CONFIG_PL2303_VENDORSTR;
+ break;
+
+ case PL2303_PRODUCTSTRID:
+ str = CONFIG_PL2303_PRODUCTSTR;
+ break;
+
+ case PL2303_SERIALSTRID:
+ str = CONFIG_PL2303_SERIALSTR;
+ break;
+
+ case PL2303_CONFIGSTRID:
+ str = CONFIG_PL2303_CONFIGSTR;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* The string is utf16-le. The poor man's utf-8 to utf16-le
+ * conversion below will only handle 7-bit en-us ascii
+ */
+
+ len = strlen(str);
+ for (i = 0, ndata = 0; i < len; i++, ndata += 2)
+ {
+ strdesc->data[ndata] = str[i];
+ strdesc->data[ndata+1] = 0;
+ }
+
+ strdesc->len = ndata+2;
+ strdesc->type = USB_DESC_TYPE_STRING;
+ return strdesc->len;
+}
+
+/****************************************************************************
+ * Name: usbclass_mkepbulkdesc
+ *
+ * Description:
+ * Construct the endpoint descriptor
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+static inline void usbclass_mkepbulkdesc(const FAR struct usb_epdesc_s *indesc,
+ uint16_t mxpacket,
+ FAR struct usb_epdesc_s *outdesc)
+{
+ /* Copy the canned descriptor */
+
+ memcpy(outdesc, indesc, USB_SIZEOF_EPDESC);
+
+ /* Then add the correct max packet size */
+
+ outdesc->mxpacketsize[0] = LSBYTE(mxpacket);
+ outdesc->mxpacketsize[1] = MSBYTE(mxpacket);
+}
+#endif
+
+/****************************************************************************
+ * Name: usbclass_mkcfgdesc
+ *
+ * Description:
+ * Construct the configuration descriptor
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+static int16_t usbclass_mkcfgdesc(uint8_t *buf, uint8_t speed, uint8_t type)
+#else
+static int16_t usbclass_mkcfgdesc(uint8_t *buf)
+#endif
+{
+ FAR struct usb_cfgdesc_s *cfgdesc = (struct usb_cfgdesc_s*)buf;
+#ifdef CONFIG_USBDEV_DUALSPEED
+ bool hispeed = (speed == USB_SPEED_HIGH);
+ uint16_t bulkmxpacket;
+#endif
+ uint16_t totallen;
+
+ /* This is the total length of the configuration (not necessarily the
+ * size that we will be sending now.
+ */
+
+ totallen = USB_SIZEOF_CFGDESC + USB_SIZEOF_IFDESC + PL2303_NENDPOINTS * USB_SIZEOF_EPDESC;
+
+ /* Configuration descriptor -- Copy the canned descriptor and fill in the
+ * type (we'll also need to update the size below
+ */
+
+ memcpy(cfgdesc, &g_cfgdesc, USB_SIZEOF_CFGDESC);
+ buf += USB_SIZEOF_CFGDESC;
+
+ /* Copy the canned interface descriptor */
+
+ memcpy(buf, &g_ifdesc, USB_SIZEOF_IFDESC);
+ buf += USB_SIZEOF_IFDESC;
+
+ /* Make the three endpoint configurations. First, check for switches
+ * between high and full speed
+ */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG)
+ {
+ hispeed = !hispeed;
+ }
+#endif
+
+ memcpy(buf, &g_epintindesc, USB_SIZEOF_EPDESC);
+ buf += USB_SIZEOF_EPDESC;
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ if (hispeed)
+ {
+ bulkmxpacket = 512;
+ }
+ else
+ {
+ bulkmxpacket = 64;
+ }
+
+ usbclass_mkepbulkdesc(&g_epbulkoutdesc, bulkmxpacket, (struct usb_epdesc_s*)buf);
+ buf += USB_SIZEOF_EPDESC;
+ usbclass_mkepbulkdesc(&g_epbulkindesc, bulkmxpacket, (struct usb_epdesc_s*)buf);
+#else
+ memcpy(buf, &g_epbulkoutdesc, USB_SIZEOF_EPDESC);
+ buf += USB_SIZEOF_EPDESC;
+ memcpy(buf, &g_epbulkindesc, USB_SIZEOF_EPDESC);
+#endif
+
+ /* Finally, fill in the total size of the configuration descriptor */
+
+ cfgdesc->totallen[0] = LSBYTE(totallen);
+ cfgdesc->totallen[1] = MSBYTE(totallen);
+ return totallen;
+}
+
+/****************************************************************************
+ * Name: usbclass_resetconfig
+ *
+ * Description:
+ * Mark the device as not configured and disable all endpoints.
+ *
+ ****************************************************************************/
+
+static void usbclass_resetconfig(FAR struct pl2303_dev_s *priv)
+{
+ /* Are we configured? */
+
+ if (priv->config != PL2303_CONFIGIDNONE)
+ {
+ /* Yes.. but not anymore */
+
+ priv->config = PL2303_CONFIGIDNONE;
+
+ /* Disable endpoints. This should force completion of all pending
+ * transfers.
+ */
+
+ EP_DISABLE(priv->epintin);
+ EP_DISABLE(priv->epbulkin);
+ EP_DISABLE(priv->epbulkout);
+ }
+}
+
+/****************************************************************************
+ * Name: usbclass_setconfig
+ *
+ * Description:
+ * Set the device configuration by allocating and configuring endpoints and
+ * by allocating and queue read and write requests.
+ *
+ ****************************************************************************/
+
+static int usbclass_setconfig(FAR struct pl2303_dev_s *priv, uint8_t config)
+{
+ FAR struct usbdev_req_s *req;
+#ifdef CONFIG_USBDEV_DUALSPEED
+ struct usb_epdesc_s epdesc;
+ uint16_t bulkmxpacket;
+#endif
+ int i;
+ int ret = 0;
+
+#if CONFIG_DEBUG
+ if (priv == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return -EIO;
+ }
+#endif
+
+ if (config == priv->config)
+ {
+ /* Already configured -- Do nothing */
+
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALREADYCONFIGURED), 0);
+ return 0;
+ }
+
+ /* Discard the previous configuration data */
+
+ usbclass_resetconfig(priv);
+
+ /* Was this a request to simply discard the current configuration? */
+
+ if (config == PL2303_CONFIGIDNONE)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONFIGNONE), 0);
+ return 0;
+ }
+
+ /* We only accept one configuration */
+
+ if (config != PL2303_CONFIGID)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONFIGIDBAD), 0);
+ return -EINVAL;
+ }
+
+ /* Configure the IN interrupt endpoint */
+
+ ret = EP_CONFIGURE(priv->epintin, &g_epintindesc, false);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPINTINCONFIGFAIL), 0);
+ goto errout;
+ }
+ priv->epintin->priv = priv;
+
+ /* Configure the IN bulk endpoint */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ if (priv->usbdev->speed == USB_SPEED_HIGH)
+ {
+ bulkmxpacket = 512;
+ }
+ else
+ {
+ bulkmxpacket = 64;
+ }
+
+ usbclass_mkepbulkdesc(&g_epbulkindesc, bulkmxpacket, &epdesc);
+ ret = EP_CONFIGURE(priv->epbulkin, &epdesc, false);
+#else
+ ret = EP_CONFIGURE(priv->epbulkin, &g_epbulkindesc, false);
+#endif
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKINCONFIGFAIL), 0);
+ goto errout;
+ }
+
+ priv->epbulkin->priv = priv;
+
+ /* Configure the OUT bulk endpoint */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ usbclass_mkepbulkdesc(&g_epbulkoutdesc, bulkmxpacket, &epdesc);
+ ret = EP_CONFIGURE(priv->epbulkout, &epdesc, true);
+#else
+ ret = EP_CONFIGURE(priv->epbulkout, &g_epbulkoutdesc, true);
+#endif
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKOUTCONFIGFAIL), 0);
+ goto errout;
+ }
+
+ priv->epbulkout->priv = priv;
+
+ /* Queue read requests in the bulk OUT endpoint */
+
+ DEBUGASSERT(priv->nrdq == 0);
+ for (i = 0; i < CONFIG_PL2303_NRDREQS; i++)
+ {
+ req = priv->rdreqs[i].req;
+ req->callback = usbclass_rdcomplete;
+ ret = EP_SUBMIT(priv->epbulkout, req);
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16_t)-ret);
+ goto errout;
+ }
+ priv->nrdq++;
+ }
+
+ priv->config = config;
+ return OK;
+
+errout:
+ usbclass_resetconfig(priv);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbclass_ep0incomplete
+ *
+ * Description:
+ * Handle completion of EP0 control operations
+ *
+ ****************************************************************************/
+
+static void usbclass_ep0incomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ if (req->result || req->xfrd != req->len)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_REQRESULT), (uint16_t)-req->result);
+ }
+}
+
+/****************************************************************************
+ * Name: usbclass_rdcomplete
+ *
+ * Description:
+ * Handle completion of read request on the bulk OUT endpoint. This
+ * is handled like the receipt of serial data on the "UART"
+ *
+ ****************************************************************************/
+
+static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ FAR struct pl2303_dev_s *priv;
+ irqstate_t flags;
+ int ret;
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (!ep || !ep->priv || !req)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract references to private data */
+
+ priv = (FAR struct pl2303_dev_s*)ep->priv;
+
+ /* Process the received data unless this is some unusual condition */
+
+ flags = irqsave();
+ switch (req->result)
+ {
+ case 0: /* Normal completion */
+ usbtrace(TRACE_CLASSRDCOMPLETE, priv->nrdq);
+ usbclass_recvpacket(priv, req->buf, req->xfrd);
+ break;
+
+ case -ESHUTDOWN: /* Disconnection */
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0);
+ priv->nrdq--;
+ irqrestore(flags);
+ return;
+
+ default: /* Some other error occurred */
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDUNEXPECTED), (uint16_t)-req->result);
+ break;
+ };
+
+ /* Requeue the read request */
+
+#ifdef CONFIG_PL2303_BULKREQLEN
+ req->len = max(CONFIG_PL2303_BULKREQLEN, ep->maxpacket);
+#else
+ req->len = ep->maxpacket;
+#endif
+
+ ret = EP_SUBMIT(ep, req);
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16_t)-req->result);
+ }
+ irqrestore(flags);
+}
+
+/****************************************************************************
+ * Name: usbclass_wrcomplete
+ *
+ * Description:
+ * Handle completion of write request. This function probably executes
+ * in the context of an interrupt handler.
+ *
+ ****************************************************************************/
+
+static void usbclass_wrcomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ FAR struct pl2303_dev_s *priv;
+ FAR struct pl2303_req_s *reqcontainer;
+ irqstate_t flags;
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (!ep || !ep->priv || !req || !req->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract references to our private data */
+
+ priv = (FAR struct pl2303_dev_s *)ep->priv;
+ reqcontainer = (FAR struct pl2303_req_s *)req->priv;
+
+ /* Return the write request to the free list */
+
+ flags = irqsave();
+ sq_addlast((sq_entry_t*)reqcontainer, &priv->reqlist);
+ priv->nwrq++;
+ irqrestore(flags);
+
+ /* Send the next packet unless this was some unusual termination
+ * condition
+ */
+
+ switch (req->result)
+ {
+ case OK: /* Normal completion */
+ usbtrace(TRACE_CLASSWRCOMPLETE, priv->nwrq);
+ usbclass_sndpacket(priv);
+ break;
+
+ case -ESHUTDOWN: /* Disconnection */
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRSHUTDOWN), priv->nwrq);
+ break;
+
+ default: /* Some other error occurred */
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRUNEXPECTED), (uint16_t)-req->result);
+ break;
+ }
+}
+
+/****************************************************************************
+ * USB Class Driver Methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbclass_bind
+ *
+ * Description:
+ * Invoked when the driver is bound to a USB device driver
+ *
+ ****************************************************************************/
+
+static int usbclass_bind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct pl2303_dev_s *priv = ((FAR struct pl2303_driver_s*)driver)->dev;
+ FAR struct pl2303_req_s *reqcontainer;
+ irqstate_t flags;
+ uint16_t reqlen;
+ int ret;
+ int i;
+
+ usbtrace(TRACE_CLASSBIND, 0);
+
+ /* Bind the structures */
+
+ priv->usbdev = dev;
+
+ /* Save the reference to our private data structure in EP0 so that it
+ * can be recovered in ep0 completion events (Unless we are part of
+ * a composite device and, in that case, the composite device owns
+ * EP0).
+ */
+
+ dev->ep0->priv = priv;
+
+ /* Preallocate control request */
+
+ priv->ctrlreq = usbclass_allocreq(dev->ep0, PL2303_MXDESCLEN);
+ if (priv->ctrlreq == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALLOCCTRLREQ), 0);
+ ret = -ENOMEM;
+ goto errout;
+ }
+ priv->ctrlreq->callback = usbclass_ep0incomplete;
+
+ /* Pre-allocate all endpoints... the endpoints will not be functional
+ * until the SET CONFIGURATION request is processed in usbclass_setconfig.
+ * This is done here because there may be calls to kmalloc and the SET
+ * CONFIGURATION processing probably occurrs within interrupt handling
+ * logic where kmalloc calls will fail.
+ */
+
+ /* Pre-allocate the IN interrupt endpoint */
+
+ priv->epintin = DEV_ALLOCEP(dev, PL2303_EPINTIN_ADDR, true, USB_EP_ATTR_XFER_INT);
+ if (!priv->epintin)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPINTINALLOCFAIL), 0);
+ ret = -ENODEV;
+ goto errout;
+ }
+ priv->epintin->priv = priv;
+
+ /* Pre-allocate the IN bulk endpoint */
+
+ priv->epbulkin = DEV_ALLOCEP(dev, PL2303_EPINBULK_ADDR, true, USB_EP_ATTR_XFER_BULK);
+ if (!priv->epbulkin)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKINALLOCFAIL), 0);
+ ret = -ENODEV;
+ goto errout;
+ }
+ priv->epbulkin->priv = priv;
+
+ /* Pre-allocate the OUT bulk endpoint */
+
+ priv->epbulkout = DEV_ALLOCEP(dev, PL2303_EPOUTBULK_ADDR, false, USB_EP_ATTR_XFER_BULK);
+ if (!priv->epbulkout)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKOUTALLOCFAIL), 0);
+ ret = -ENODEV;
+ goto errout;
+ }
+ priv->epbulkout->priv = priv;
+
+ /* Pre-allocate read requests */
+
+#ifdef CONFIG_PL2303_BULKREQLEN
+ reqlen = max(CONFIG_PL2303_BULKREQLEN, priv->epbulkout->maxpacket);
+#else
+ reqlen = priv->epbulkout->maxpacket;
+#endif
+
+ for (i = 0; i < CONFIG_PL2303_NRDREQS; i++)
+ {
+ reqcontainer = &priv->rdreqs[i];
+ reqcontainer->req = usbclass_allocreq(priv->epbulkout, reqlen);
+ if (reqcontainer->req == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDALLOCREQ), -ENOMEM);
+ ret = -ENOMEM;
+ goto errout;
+ }
+ reqcontainer->req->priv = reqcontainer;
+ reqcontainer->req->callback = usbclass_rdcomplete;
+ }
+
+ /* Pre-allocate write request containers and put in a free list */
+
+#ifdef CONFIG_PL2303_BULKREQLEN
+ reqlen = max(CONFIG_PL2303_BULKREQLEN, priv->epbulkin->maxpacket);
+#else
+ reqlen = priv->epbulkin->maxpacket;
+#endif
+
+ for (i = 0; i < CONFIG_PL2303_NWRREQS; i++)
+ {
+ reqcontainer = &priv->wrreqs[i];
+ reqcontainer->req = usbclass_allocreq(priv->epbulkin, reqlen);
+ if (reqcontainer->req == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRALLOCREQ), -ENOMEM);
+ ret = -ENOMEM;
+ goto errout;
+ }
+ reqcontainer->req->priv = reqcontainer;
+ reqcontainer->req->callback = usbclass_wrcomplete;
+
+ flags = irqsave();
+ sq_addlast((sq_entry_t*)reqcontainer, &priv->reqlist);
+ priv->nwrq++; /* Count of write requests available */
+ irqrestore(flags);
+ }
+
+ /* Report if we are selfpowered */
+
+#ifdef CONFIG_USBDEV_SELFPOWERED
+ DEV_SETSELFPOWERED(dev);
+#endif
+
+ /* And pull-up the data line for the soft connect function */
+
+ DEV_CONNECT(dev);
+ return OK;
+
+errout:
+ usbclass_unbind(driver, dev);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbclass_unbind
+ *
+ * Description:
+ * Invoked when the driver is unbound from a USB device driver
+ *
+ ****************************************************************************/
+
+static void usbclass_unbind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct pl2303_dev_s *priv;
+ FAR struct pl2303_req_s *reqcontainer;
+ irqstate_t flags;
+ int i;
+
+ usbtrace(TRACE_CLASSUNBIND, 0);
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev || !dev->ep0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = ((FAR struct pl2303_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
+ return;
+ }
+#endif
+
+ /* Make sure that we are not already unbound */
+
+ if (priv != NULL)
+ {
+ /* Make sure that the endpoints have been unconfigured. If
+ * we were terminated gracefully, then the configuration should
+ * already have been reset. If not, then calling usbclass_resetconfig
+ * should cause the endpoints to immediately terminate all
+ * transfers and return the requests to us (with result == -ESHUTDOWN)
+ */
+
+ usbclass_resetconfig(priv);
+ up_mdelay(50);
+
+ /* Free the interrupt IN endpoint */
+
+ if (priv->epintin)
+ {
+ DEV_FREEEP(dev, priv->epintin);
+ priv->epintin = NULL;
+ }
+
+ /* Free the bulk IN endpoint */
+
+ if (priv->epbulkin)
+ {
+ DEV_FREEEP(dev, priv->epbulkin);
+ priv->epbulkin = NULL;
+ }
+
+ /* Free the pre-allocated control request */
+
+ if (priv->ctrlreq != NULL)
+ {
+ usbclass_freereq(dev->ep0, priv->ctrlreq);
+ priv->ctrlreq = NULL;
+ }
+
+ /* Free pre-allocated read requests (which should all have
+ * been returned to the free list at this time -- we don't check)
+ */
+
+ DEBUGASSERT(priv->nrdq == 0);
+ for (i = 0; i < CONFIG_PL2303_NRDREQS; i++)
+ {
+ reqcontainer = &priv->rdreqs[i];
+ if (reqcontainer->req)
+ {
+ usbclass_freereq(priv->epbulkout, reqcontainer->req);
+ reqcontainer->req = NULL;
+ }
+ }
+
+ /* Free the bulk OUT endpoint */
+
+ if (priv->epbulkout)
+ {
+ DEV_FREEEP(dev, priv->epbulkout);
+ priv->epbulkout = NULL;
+ }
+
+ /* Free write requests that are not in use (which should be all
+ * of them
+ */
+
+ flags = irqsave();
+ DEBUGASSERT(priv->nwrq == CONFIG_PL2303_NWRREQS);
+ while (!sq_empty(&priv->reqlist))
+ {
+ reqcontainer = (struct pl2303_req_s *)sq_remfirst(&priv->reqlist);
+ if (reqcontainer->req != NULL)
+ {
+ usbclass_freereq(priv->epbulkin, reqcontainer->req);
+ priv->nwrq--; /* Number of write requests queued */
+ }
+ }
+ DEBUGASSERT(priv->nwrq == 0);
+ irqrestore(flags);
+ }
+
+ /* Clear out all data in the circular buffer */
+
+ priv->serdev.xmit.head = 0;
+ priv->serdev.xmit.tail = 0;
+}
+
+/****************************************************************************
+ * Name: usbclass_setup
+ *
+ * Description:
+ * Invoked for ep0 control requests. This function probably executes
+ * in the context of an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int usbclass_setup(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl,
+ FAR uint8_t *dataout, size_t outlen)
+{
+ FAR struct pl2303_dev_s *priv;
+ FAR struct usbdev_req_s *ctrlreq;
+ uint16_t value;
+ uint16_t index;
+ uint16_t len;
+ int ret = -EOPNOTSUPP;
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev || !dev->ep0 || !ctrl)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return -EIO;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ usbtrace(TRACE_CLASSSETUP, ctrl->req);
+ priv = ((FAR struct pl2303_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv || !priv->ctrlreq)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
+ return -ENODEV;
+ }
+#endif
+ ctrlreq = priv->ctrlreq;
+
+ /* Extract the little-endian 16-bit values to host order */
+
+ value = GETUINT16(ctrl->value);
+ index = GETUINT16(ctrl->index);
+ len = GETUINT16(ctrl->len);
+
+ uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
+ ctrl->type, ctrl->req, value, index, len);
+
+ switch (ctrl->type & USB_REQ_TYPE_MASK)
+ {
+ /***********************************************************************
+ * Standard Requests
+ ***********************************************************************/
+
+ case USB_REQ_TYPE_STANDARD:
+ {
+ switch (ctrl->req)
+ {
+ case USB_REQ_GETDESCRIPTOR:
+ {
+ /* The value field specifies the descriptor type in the MS byte and the
+ * descriptor index in the LS byte (order is little endian)
+ */
+
+ switch (ctrl->value[1])
+ {
+ case USB_DESC_TYPE_DEVICE:
+ {
+ ret = USB_SIZEOF_DEVDESC;
+ memcpy(ctrlreq->buf, &g_devdesc, ret);
+ }
+ break;
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ case USB_DESC_TYPE_DEVICEQUALIFIER:
+ {
+ ret = USB_SIZEOF_QUALDESC;
+ memcpy(ctrlreq->buf, &g_qualdesc, ret);
+ }
+ break;
+
+ case USB_DESC_TYPE_OTHERSPEEDCONFIG:
+#endif /* CONFIG_USBDEV_DUALSPEED */
+
+ case USB_DESC_TYPE_CONFIG:
+ {
+#ifdef CONFIG_USBDEV_DUALSPEED
+ ret = usbclass_mkcfgdesc(ctrlreq->buf, dev->speed, ctrl->req);
+#else
+ ret = usbclass_mkcfgdesc(ctrlreq->buf);
+#endif
+ }
+ break;
+
+ case USB_DESC_TYPE_STRING:
+ {
+ /* index == language code. */
+
+ ret = usbclass_mkstrdesc(ctrl->value[0], (struct usb_strdesc_s *)ctrlreq->buf);
+ }
+ break;
+
+ default:
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_GETUNKNOWNDESC), value);
+ }
+ break;
+ }
+ }
+ break;
+
+ case USB_REQ_SETCONFIGURATION:
+ {
+ if (ctrl->type == 0)
+ {
+ ret = usbclass_setconfig(priv, value);
+ }
+ }
+ break;
+
+ case USB_REQ_GETCONFIGURATION:
+ {
+ if (ctrl->type == USB_DIR_IN)
+ {
+ *(uint8_t*)ctrlreq->buf = priv->config;
+ ret = 1;
+ }
+ }
+ break;
+
+ case USB_REQ_SETINTERFACE:
+ {
+ if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE)
+ {
+ if (priv->config == PL2303_CONFIGID &&
+ index == PL2303_INTERFACEID &&
+ value == PL2303_ALTINTERFACEID)
+ {
+ usbclass_resetconfig(priv);
+ usbclass_setconfig(priv, priv->config);
+ ret = 0;
+ }
+ }
+ }
+ break;
+
+ case USB_REQ_GETINTERFACE:
+ {
+ if (ctrl->type == (USB_DIR_IN|USB_REQ_RECIPIENT_INTERFACE) &&
+ priv->config == PL2303_CONFIGIDNONE)
+ {
+ if (index != PL2303_INTERFACEID)
+ {
+ ret = -EDOM;
+ }
+ else
+ {
+ *(uint8_t*) ctrlreq->buf = PL2303_ALTINTERFACEID;
+ ret = 1;
+ }
+ }
+ }
+ break;
+
+ default:
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req);
+ break;
+ }
+ }
+ break;
+
+ /***********************************************************************
+ * PL2303 Vendor-Specific Requests
+ ***********************************************************************/
+
+ case PL2303_CONTROL_TYPE:
+ {
+ if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_INTERFACE)
+ {
+ switch (ctrl->req)
+ {
+ case PL2303_SETLINEREQUEST:
+ {
+ memcpy(priv->linest, ctrlreq->buf, min(len, 7));
+ ret = 0;
+ }
+ break;
+
+
+ case PL2303_GETLINEREQUEST:
+ {
+ memcpy(ctrlreq->buf, priv->linest, 7);
+ ret = 7;
+ }
+ break;
+
+ case PL2303_SETCONTROLREQUEST:
+ case PL2303_BREAKREQUEST:
+ {
+ ret = 0;
+ }
+ break;
+
+ default:
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type);
+ break;
+ }
+ }
+ }
+ break;
+
+ case PL2303_RWREQUEST_TYPE:
+ {
+ if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE)
+ {
+ if (ctrl->req == PL2303_RWREQUEST)
+ {
+ if ((ctrl->type & USB_DIR_IN) != 0)
+ {
+ *(uint32_t*)ctrlreq->buf = 0xdeadbeef;
+ ret = 4;
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+ else
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type);
+ }
+ }
+ }
+ break;
+
+ default:
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDTYPE), ctrl->type);
+ break;
+ }
+
+ /* Respond to the setup command if data was returned. On an error return
+ * value (ret < 0), the USB driver will stall.
+ */
+
+ if (ret >= 0)
+ {
+ ctrlreq->len = min(len, ret);
+ ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;
+ ret = EP_SUBMIT(dev->ep0, ctrlreq);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPRESPQ), (uint16_t)-ret);
+ ctrlreq->result = OK;
+ usbclass_ep0incomplete(dev->ep0, ctrlreq);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbclass_disconnect
+ *
+ * Description:
+ * Invoked after all transfers have been stopped, when the host is
+ * disconnected. This function is probably called from the context of an
+ * interrupt handler.
+ *
+ ****************************************************************************/
+
+static void usbclass_disconnect(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct pl2303_dev_s *priv;
+ irqstate_t flags;
+
+ usbtrace(TRACE_CLASSDISCONNECT, 0);
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev || !dev->ep0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = ((FAR struct pl2303_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
+ return;
+ }
+#endif
+
+ /* Reset the configuration */
+
+ flags = irqsave();
+ usbclass_resetconfig(priv);
+
+ /* Clear out all data in the circular buffer */
+
+ priv->serdev.xmit.head = 0;
+ priv->serdev.xmit.tail = 0;
+ irqrestore(flags);
+
+ /* Perform the soft connect function so that we will we can be
+ * re-enumerated.
+ */
+
+ DEV_CONNECT(dev);
+}
+
+/****************************************************************************
+ * Serial Device Methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbser_setup
+ *
+ * Description:
+ * This method is called the first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int usbser_setup(FAR struct uart_dev_s *dev)
+{
+ FAR struct pl2303_dev_s *priv;
+
+ usbtrace(PL2303_CLASSASPI_SETUP, 0);
+
+ /* Sanity check */
+
+#if CONFIG_DEBUG
+ if (!dev || !dev->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return -EIO;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = (FAR struct pl2303_dev_s*)dev->priv;
+
+ /* Check if we have been configured */
+
+ if (priv->config == PL2303_CONFIGIDNONE)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SETUPNOTCONNECTED), 0);
+ return -ENOTCONN;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbser_shutdown
+ *
+ * Description:
+ * This method is called when the serial port is closed. This operation
+ * is very simple for the USB serial backend because the serial driver
+ * has already assured that the TX data has full drained -- it calls
+ * usbser_txempty() until that function returns true before calling this
+ * function.
+ *
+ ****************************************************************************/
+
+static void usbser_shutdown(FAR struct uart_dev_s *dev)
+{
+ usbtrace(PL2303_CLASSASPI_SHUTDOWN, 0);
+
+ /* Sanity check */
+
+#if CONFIG_DEBUG
+ if (!dev || !dev->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ }
+#endif
+}
+
+/****************************************************************************
+ * Name: usbser_attach
+ *
+ * Description:
+ * Does not apply to the USB serial class device
+ *
+ ****************************************************************************/
+
+static int usbser_attach(FAR struct uart_dev_s *dev)
+{
+ usbtrace(PL2303_CLASSASPI_ATTACH, 0);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbser_detach
+ *
+ * Description:
+* Does not apply to the USB serial class device
+ *
+ ****************************************************************************/
+
+static void usbser_detach(FAR struct uart_dev_s *dev)
+{
+ usbtrace(PL2303_CLASSASPI_DETACH, 0);
+}
+
+/****************************************************************************
+ * Name: usbser_rxint
+ *
+ * Description:
+ * Called by the serial driver to enable or disable RX interrupts. We, of
+ * course, have no RX interrupts but must behave consistently. This method
+ * is called under the conditions:
+ *
+ * 1. With enable==true when the port is opened (just after usbser_setup
+ * and usbser_attach are called called)
+ * 2. With enable==false while transferring data from the RX buffer
+ * 2. With enable==true while waiting for more incoming data
+ * 3. With enable==false when the port is closed (just before usbser_detach
+ * and usbser_shutdown are called).
+ *
+ ****************************************************************************/
+
+static void usbser_rxint(FAR struct uart_dev_s *dev, bool enable)
+{
+ FAR struct pl2303_dev_s *priv;
+ FAR uart_dev_t *serdev;
+ irqstate_t flags;
+
+ usbtrace(PL2303_CLASSASPI_RXINT, (uint16_t)enable);
+
+ /* Sanity check */
+
+#if CONFIG_DEBUG
+ if (!dev || !dev->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = (FAR struct pl2303_dev_s*)dev->priv;
+ serdev = &priv->serdev;
+
+ /* We need exclusive access to the RX buffer and private structure
+ * in the following.
+ */
+
+ flags = irqsave();
+ if (enable)
+ {
+ /* RX "interrupts" are enabled. Is this a transition from disabled
+ * to enabled state?
+ */
+
+ if (!priv->rxenabled)
+ {
+ /* Yes. During the time that RX interrupts are disabled, the
+ * the serial driver will be extracting data from the circular
+ * buffer and modifying recv.tail. During this time, we
+ * should avoid modifying recv.head; When interrupts are restored,
+ * we can update the head pointer for all of the data that we
+ * put into cicular buffer while "interrupts" were disabled.
+ */
+
+ if (priv->rxhead != serdev->recv.head)
+ {
+ serdev->recv.head = priv->rxhead;
+
+ /* Yes... signal the availability of new data */
+
+ uart_datareceived(serdev);
+ }
+
+ /* RX "interrupts are no longer disabled */
+
+ priv->rxenabled = true;
+ }
+ }
+
+ /* RX "interrupts" are disabled. Is this a transition from enabled
+ * to disabled state?
+ */
+
+ else if (priv->rxenabled)
+ {
+ /* Yes. During the time that RX interrupts are disabled, the
+ * the serial driver will be extracting data from the circular
+ * buffer and modifying recv.tail. During this time, we
+ * should avoid modifying recv.head; When interrupts are disabled,
+ * we use a shadow index and continue adding data to the circular
+ * buffer.
+ */
+
+ priv->rxhead = serdev->recv.head;
+ priv->rxenabled = false;
+ }
+ irqrestore(flags);
+}
+
+/****************************************************************************
+ * Name: usbser_txint
+ *
+ * Description:
+ * Called by the serial driver to enable or disable TX interrupts. We, of
+ * course, have no TX interrupts but must behave consistently. Initially,
+ * TX interrupts are disabled. This method is called under the conditions:
+ *
+ * 1. With enable==false while transferring data into the TX buffer
+ * 2. With enable==true when data may be taken from the buffer.
+ * 3. With enable==false when the TX buffer is empty
+ *
+ ****************************************************************************/
+
+static void usbser_txint(FAR struct uart_dev_s *dev, bool enable)
+{
+ FAR struct pl2303_dev_s *priv;
+
+ usbtrace(PL2303_CLASSASPI_TXINT, (uint16_t)enable);
+
+ /* Sanity checks */
+
+#if CONFIG_DEBUG
+ if (!dev || !dev->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return;
+ }
+#endif
+
+ /* Extract references to private data */
+
+ priv = (FAR struct pl2303_dev_s*)dev->priv;
+
+ /* If the new state is enabled and if there is data in the XMIT buffer,
+ * send the next packet now.
+ */
+
+ uvdbg("enable=%d head=%d tail=%d\n",
+ enable, priv->serdev.xmit.head, priv->serdev.xmit.tail);
+
+ if (enable && priv->serdev.xmit.head != priv->serdev.xmit.tail)
+ {
+ usbclass_sndpacket(priv);
+ }
+}
+
+/****************************************************************************
+ * Name: usbser_txempty
+ *
+ * Description:
+ * Return true when all data has been sent. This is called from the
+ * serial driver when the driver is closed. It will call this API
+ * periodically until it reports true. NOTE that the serial driver takes all
+ * responsibility for flushing TX data through the hardware so we can be
+ * a bit sloppy about that.
+ *
+ ****************************************************************************/
+
+static bool usbser_txempty(FAR struct uart_dev_s *dev)
+{
+ FAR struct pl2303_dev_s *priv = (FAR struct pl2303_dev_s*)dev->priv;
+
+ usbtrace(PL2303_CLASSASPI_TXEMPTY, 0);
+
+#if CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
+ return true;
+ }
+#endif
+
+ /* When all of the allocated write requests have been returned to the
+ * reqlist, then there is no longer any TX data in flight.
+ */
+
+ return priv->nwrq >= CONFIG_PL2303_NWRREQS;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbdev_serialinitialize
+ *
+ * Description:
+ * Register USB serial port (and USB serial console if so configured).
+ *
+ ****************************************************************************/
+
+int usbdev_serialinitialize(int minor)
+{
+ FAR struct pl2303_alloc_s *alloc;
+ FAR struct pl2303_dev_s *priv;
+ FAR struct pl2303_driver_s *drvr;
+ char devname[16];
+ int ret;
+
+ /* Allocate the structures needed */
+
+ alloc = (FAR struct pl2303_alloc_s*)kmalloc(sizeof(struct pl2303_alloc_s));
+ if (!alloc)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALLOCDEVSTRUCT), 0);
+ return -ENOMEM;
+ }
+
+ /* Convenience pointers into the allocated blob */
+
+ priv = &alloc->dev;
+ drvr = &alloc->drvr;
+
+ /* Initialize the USB serial driver structure */
+
+ memset(priv, 0, sizeof(struct pl2303_dev_s));
+ sq_init(&priv->reqlist);
+
+ /* Fake line status */
+
+ priv->linest[0] = (115200) & 0xff; /* Baud=115200 */
+ priv->linest[1] = (115200 >> 8) & 0xff;
+ priv->linest[2] = (115200 >> 16) & 0xff;
+ priv->linest[3] = (115200 >> 24) & 0xff;
+ priv->linest[4] = 0; /* One stop bit */
+ priv->linest[5] = 0; /* No parity */
+ priv->linest[6] = 8; /*8 data bits */
+
+ /* Initialize the serial driver sub-structure */
+
+ priv->serdev.recv.size = CONFIG_PL2303_RXBUFSIZE;
+ priv->serdev.recv.buffer = priv->rxbuffer;
+ priv->serdev.xmit.size = CONFIG_PL2303_TXBUFSIZE;
+ priv->serdev.xmit.buffer = priv->txbuffer;
+ priv->serdev.ops = &g_uartops;
+ priv->serdev.priv = priv;
+
+ /* Initialize the USB class driver structure */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ drvr->drvr.speed = USB_SPEED_HIGH;
+#else
+ drvr->drvr.speed = USB_SPEED_FULL;
+#endif
+ drvr->drvr.ops = &g_driverops;
+ drvr->dev = priv;
+
+ /* Register the USB serial class driver */
+
+ ret = usbdev_register(&drvr->drvr);
+ if (ret)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_DEVREGISTER), (uint16_t)-ret);
+ goto errout_with_alloc;
+ }
+
+ /* Register the USB serial console */
+
+#ifdef CONFIG_PL2303_CONSOLE
+ priv->serdev.isconsole = true;
+ ret = uart_register("/dev/console", &priv->serdev);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONSOLEREGISTER), (uint16_t)-ret);
+ goto errout_with_class;
+ }
+#endif
+
+ /* Register the single port supported by this implementation */
+
+ sprintf(devname, "/dev/ttyUSB%d", minor);
+ ret = uart_register(devname, &priv->serdev);
+ if (ret)
+ {
+ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UARTREGISTER), (uint16_t)-ret);
+ goto errout_with_class;
+ }
+ return OK;
+
+errout_with_class:
+ usbdev_unregister(&drvr->drvr);
+errout_with_alloc:
+ kfree(alloc);
+ return ret;
+}
diff --git a/nuttx/drivers/usbdev/usbdev_trace.c b/nuttx/drivers/usbdev/usbdev_trace.c
new file mode 100644
index 000000000..c8cc09292
--- /dev/null
+++ b/nuttx/drivers/usbdev/usbdev_trace.c
@@ -0,0 +1,233 @@
+/****************************************************************************
+ * drivers/usbdev/usbdev_trace.c
+ *
+ * Copyright (C) 2008-2010, 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/irq.h>
+#include <nuttx/usb/usbdev_trace.h>
+#undef usbtrace
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_USBDEV_TRACE_NRECORDS
+# define CONFIG_USBDEV_TRACE_NRECORDS 128
+#endif
+
+#ifndef CONFIG_USBDEV_TRACE_INITIALIDSET
+# define CONFIG_USBDEV_TRACE_INITIALIDSET 0
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_TRACE
+static struct usbtrace_s g_trace[CONFIG_USBDEV_TRACE_NRECORDS];
+static uint16_t g_head = 0;
+static uint16_t g_tail = 0;
+#endif
+
+#if defined(CONFIG_USBDEV_TRACE) || (defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_USB))
+static usbtrace_idset_t g_maskedidset = CONFIG_USBDEV_TRACE_INITIALIDSET;
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/*******************************************************************************
+ * Name: usbtrace_enable
+ *
+ * Description:
+ * Enable/disable tracing per trace ID. The initial state is all IDs enabled.
+ *
+ * Input Parameters:
+ * idset - The bitset of IDs to be masked. TRACE_ALLIDS enables all IDS; zero
+ * masks all IDs.
+ *
+ * Returned Value:
+ * The previous idset value.
+ *
+ * Assumptions:
+ * - May be called from an interrupt handler
+ *
+ *******************************************************************************/
+
+#if defined(CONFIG_USBDEV_TRACE) || (defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_USB))
+usbtrace_idset_t usbtrace_enable(usbtrace_idset_t idset)
+{
+ irqstate_t flags;
+ usbtrace_idset_t ret;
+
+ /* The following read and write must be atomic */
+
+ flags = irqsave();
+ ret = g_maskedidset;
+ g_maskedidset = idset;
+ irqrestore(flags);
+ return ret;
+}
+#endif /* CONFIG_USBDEV_TRACE || CONFIG_DEBUG && CONFIG_DEBUG_USB */
+
+/*******************************************************************************
+ * Name: usbtrace
+ *
+ * Description:
+ * Record a USB event (tracing must be enabled)
+ *
+ * Assumptions:
+ * May be called from an interrupt handler
+ *
+ *******************************************************************************/
+
+#if defined(CONFIG_USBDEV_TRACE) || (defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_USB))
+void usbtrace(uint16_t event, uint16_t value)
+{
+ irqstate_t flags;
+
+ /* Check if tracing is enabled for this ID */
+
+ flags = irqsave();
+ if ((g_maskedidset & TRACE_ID2BIT(event)) != 0)
+ {
+#ifdef CONFIG_USBDEV_TRACE
+ /* Yes... save the new trace data at the head */
+
+ g_trace[g_head].event = event;
+ g_trace[g_head].value = value;
+
+ /* Increment the head and (probably) the tail index */
+
+ if (++g_head >= CONFIG_USBDEV_TRACE_NRECORDS)
+ {
+ g_head = 0;
+ }
+
+ if (g_head == g_tail)
+ {
+ if (++g_tail >= CONFIG_USBDEV_TRACE_NRECORDS)
+ {
+ g_tail = 0;
+ }
+ }
+#else
+ /* Just print the data using lib_lowprintf */
+
+ usbtrace_trprintf((trprintf_t)lib_lowprintf, event, value);
+#endif
+ }
+ irqrestore(flags);
+}
+#endif /* CONFIG_USBDEV_TRACE || CONFIG_DEBUG && CONFIG_DEBUG_USB */
+
+/*******************************************************************************
+ * Name: usbtrace_enumerate
+ *
+ * Description:
+ * Enumerate all buffer trace data (will temporarily disable tracing)
+ *
+ * Assumptions:
+ * NEVER called from an interrupt handler
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_USBDEV_TRACE
+int usbtrace_enumerate(trace_callback_t callback, void *arg)
+{
+ uint16_t ndx;
+ uint32_t idset;
+ int ret = OK;
+
+ /* Temporarily disable tracing */
+
+ idset = usbtrace_enable(0);
+
+ /* Visit every entry, starting with the tail */
+
+ for (ndx = g_tail; ndx != g_head; )
+ {
+ /* Call the user provided callback */
+
+ ret = callback(&g_trace[ndx], arg);
+ if (ret != OK)
+ {
+ /* Abort the enumeration */
+
+ break;
+ }
+
+ /* Increment the index */
+
+ if (++ndx >= CONFIG_USBDEV_TRACE_NRECORDS)
+ {
+ ndx = 0;
+ }
+ }
+
+ /* Discard the trace data after it has been reported */
+
+ g_tail = g_head;
+
+ /* Restore tracing state */
+
+ (void)usbtrace_enable(idset);
+ return ret;
+}
+#endif /* CONFIG_USBDEV_TRACE */
diff --git a/nuttx/drivers/usbdev/usbdev_trprintf.c b/nuttx/drivers/usbdev/usbdev_trprintf.c
new file mode 100644
index 000000000..2a9921f98
--- /dev/null
+++ b/nuttx/drivers/usbdev/usbdev_trprintf.c
@@ -0,0 +1,253 @@
+/****************************************************************************
+ * drivers/usbdev/usbdev_trprintf.c
+ *
+ * Copyright (C) 2008-2010, 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <debug.h>
+
+#include <nuttx/usb/usbdev_trace.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/*******************************************************************************
+ * Name: usbtrace_trprintf
+ *
+ * Description:
+ * Print the trace record using the supplied printing function
+ *
+ *******************************************************************************/
+
+void usbtrace_trprintf(trprintf_t trprintf, uint16_t event, uint16_t value)
+{
+ switch (event)
+ {
+ case TRACE_DEVINIT:
+ trprintf("USB controller initialization: %04x\n", value);
+ break;
+
+ case TRACE_DEVUNINIT:
+ trprintf("USB controller un-initialization: %04x\n", value);
+ break;
+
+ case TRACE_DEVREGISTER:
+ trprintf("usbdev_register(): %04x\n", value);
+ break;
+
+ case TRACE_DEVUNREGISTER:
+ trprintf("usbdev_unregister(): %04x\n", value);
+ break;
+
+ case TRACE_EPCONFIGURE:
+ trprintf("Endpoint configure(): %04x\n", value);
+ break;
+
+ case TRACE_EPDISABLE:
+ trprintf("Endpoint disable(): %04x\n", value);
+ break;
+
+ case TRACE_EPALLOCREQ:
+ trprintf("Endpoint allocreq(): %04x\n", value);
+ break;
+
+ case TRACE_EPFREEREQ:
+ trprintf("Endpoint freereq(): %04x\n", value);
+ break;
+
+ case TRACE_EPALLOCBUFFER:
+ trprintf("Endpoint allocbuffer(): %04x\n", value);
+ break;
+
+ case TRACE_EPFREEBUFFER:
+ trprintf("Endpoint freebuffer(): %04x\n", value);
+ break;
+
+ case TRACE_EPSUBMIT:
+ trprintf("Endpoint submit(): %04x\n", value);
+ break;
+
+ case TRACE_EPCANCEL:
+ trprintf("Endpoint cancel(): %04x\n", value);
+ break;
+
+ case TRACE_EPSTALL:
+ trprintf("Endpoint stall(true): %04x\n", value);
+ break;
+
+ case TRACE_EPRESUME:
+ trprintf("Endpoint stall(false): %04x\n", value);
+ break;
+
+ case TRACE_DEVALLOCEP:
+ trprintf("Device allocep(): %04x\n", value);
+ break;
+
+ case TRACE_DEVFREEEP:
+ trprintf("Device freeep(): %04x\n", value);
+ break;
+
+ case TRACE_DEVGETFRAME:
+ trprintf("Device getframe(): %04x\n", value);
+ break;
+
+ case TRACE_DEVWAKEUP:
+ trprintf("Device wakeup(): %04x\n", value);
+ break;
+
+ case TRACE_DEVSELFPOWERED:
+ trprintf("Device selfpowered(): %04x\n", value);
+ break;
+
+ case TRACE_DEVPULLUP:
+ trprintf("Device pullup(): %04x\n", value);
+ break;
+
+ case TRACE_CLASSBIND:
+ trprintf("Class bind(): %04x\n", value);
+ break;
+
+ case TRACE_CLASSUNBIND:
+ trprintf("Class unbind(): %04x\n", value);
+ break;
+
+ case TRACE_CLASSDISCONNECT:
+ trprintf("Class disconnect(): %04x\n", value);
+ break;
+
+ case TRACE_CLASSSETUP:
+ trprintf("Class setup(): %04x\n", value);
+ break;
+
+ case TRACE_CLASSSUSPEND:
+ trprintf("Class suspend(): %04x\n", value);
+ break;
+
+ case TRACE_CLASSRESUME:
+ trprintf("Class resume(): %04x\n", value);
+ break;
+
+ case TRACE_CLASSRDCOMPLETE:
+ trprintf("Class RD request complete: %04x\n", value);
+ break;
+
+ case TRACE_CLASSWRCOMPLETE:
+ trprintf("Class WR request complete: %04x\n", value);
+ break;
+
+ default:
+ switch (TRACE_ID(event))
+ {
+ case TRACE_CLASSAPI_ID: /* Other class driver system API calls */
+ trprintf("Class API call %d: %04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_CLASSSTATE_ID: /* Track class driver state changes */
+ trprintf("Class state %d: %04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_INTENTRY_ID: /* Interrupt handler entry */
+ trprintf("Interrupt %d entry: %04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_INTDECODE_ID: /* Decoded interrupt event */
+ trprintf("Interrupt decode %d: %04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_INTEXIT_ID: /* Interrupt handler exit */
+ trprintf("Interrupt %d exit: %04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_OUTREQQUEUED_ID: /* Request queued for OUT endpoint */
+ trprintf("EP%d OUT request queued: %04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_INREQQUEUED_ID: /* Request queued for IN endpoint */
+ trprintf("EP%d IN request queued: %04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_READ_ID: /* Read (OUT) action */
+ trprintf("EP%d OUT read: %04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_WRITE_ID: /* Write (IN) action */
+ trprintf("EP%d IN write: %04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_COMPLETE_ID: /* Request completed */
+ trprintf("EP%d request complete: %04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_DEVERROR_ID: /* USB controller driver error event */
+ trprintf("Controller error: %02x:%04x\n", TRACE_DATA(event), value);
+ break;
+
+ case TRACE_CLSERROR_ID: /* USB class driver error event */
+ trprintf("Class error: %02x:%04x\n", TRACE_DATA(event), value);
+ break;
+
+ default:
+ trprintf("Unrecognized event: %02x:%02x:%04x\n",
+ TRACE_ID(event) >> 8, TRACE_DATA(event), value);
+ break;
+ }
+ }
+}
diff --git a/nuttx/drivers/usbdev/usbmsc.c b/nuttx/drivers/usbdev/usbmsc.c
new file mode 100644
index 000000000..68b61814a
--- /dev/null
+++ b/nuttx/drivers/usbdev/usbmsc.c
@@ -0,0 +1,1778 @@
+/****************************************************************************
+ * drivers/usbdev/usbmsc.c
+ *
+ * Copyright (C) 2008-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Mass storage class device. Bulk-only with SCSI subclass.
+ *
+ * References:
+ * "Universal Serial Bus Mass Storage Class, Specification Overview,"
+ * Revision 1.2, USB Implementer's Forum, June 23, 2003.
+ *
+ * "Universal Serial Bus Mass Storage Class, Bulk-Only Transport,"
+ * Revision 1.0, USB Implementer's Forum, September 31, 1999.
+ *
+ * "SCSI Primary Commands - 3 (SPC-3)," American National Standard
+ * for Information Technology, May 4, 2005
+ *
+ * "SCSI Primary Commands - 4 (SPC-4)," American National Standard
+ * for Information Technology, July 19, 2008
+ *
+ * "SCSI Block Commands -2 (SBC-2)," American National Standard
+ * for Information Technology, November 13, 2004
+ *
+ * "SCSI Multimedia Commands - 3 (MMC-3)," American National Standard
+ * for Information Technology, November 12, 2001
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+#include <errno.h>
+#include <queue.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/storage.h>
+#include <nuttx/usb/usbdev.h>
+#include <nuttx/usb/usbdev_trace.h>
+
+#include "usbmsc.h"
+
+#ifdef CONFIG_USBMSC_COMPOSITE
+# include <nuttx/usb/composite.h>
+# include "composite.h"
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The internal version of the class driver */
+
+struct usbmsc_driver_s
+{
+ struct usbdevclass_driver_s drvr;
+ FAR struct usbmsc_dev_s *dev;
+};
+
+/* This is what is allocated */
+
+struct usbmsc_alloc_s
+{
+ struct usbmsc_dev_s dev;
+ struct usbmsc_driver_s drvr;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Class Driver Support *****************************************************/
+
+static void usbmsc_ep0incomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+static struct usbdev_req_s *usbmsc_allocreq(FAR struct usbdev_ep_s *ep,
+ uint16_t len);
+static void usbmsc_freereq(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+
+/* Class Driver Operations (most at interrupt level) ************************/
+
+static int usbmsc_bind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+static void usbmsc_unbind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+static int usbmsc_setup(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout,
+ size_t outlen);
+static void usbmsc_disconnect(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev);
+
+/* Initialization/Uninitialization ******************************************/
+
+static void usbmsc_lununinitialize(struct usbmsc_lun_s *lun);
+#ifdef CONFIG_USBMSC_COMPOSITE
+static int usbmsc_exportluns(FAR void *handle);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Driver operations ********************************************************/
+
+static struct usbdevclass_driverops_s g_driverops =
+{
+ usbmsc_bind, /* bind */
+ usbmsc_unbind, /* unbind */
+ usbmsc_setup, /* setup */
+ usbmsc_disconnect, /* disconnect */
+ NULL, /* suspend */
+ NULL /* resume */
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Class Driver Support
+ ****************************************************************************/
+/****************************************************************************
+ * Name: usbmsc_ep0incomplete
+ *
+ * Description:
+ * Handle completion of EP0 control operations
+ *
+ ****************************************************************************/
+
+static void usbmsc_ep0incomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req)
+{
+ if (req->result || req->xfrd != req->len)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_REQRESULT),
+ (uint16_t)-req->result);
+ }
+}
+
+/****************************************************************************
+ * Name: usbmsc_allocreq
+ *
+ * Description:
+ * Allocate a request instance along with its buffer
+ *
+ ****************************************************************************/
+
+static struct usbdev_req_s *usbmsc_allocreq(FAR struct usbdev_ep_s *ep,
+ uint16_t len)
+{
+ FAR struct usbdev_req_s *req;
+
+ req = EP_ALLOCREQ(ep);
+ if (req != NULL)
+ {
+ req->len = len;
+ req->buf = EP_ALLOCBUFFER(ep, len);
+ if (!req->buf)
+ {
+ EP_FREEREQ(ep, req);
+ req = NULL;
+ }
+ }
+ return req;
+}
+
+/****************************************************************************
+ * Name: usbmsc_freereq
+ *
+ * Description:
+ * Free a request instance along with its buffer
+ *
+ ****************************************************************************/
+
+static void usbmsc_freereq(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req)
+{
+ if (ep != NULL && req != NULL)
+ {
+ if (req->buf != NULL)
+ {
+ EP_FREEBUFFER(ep, req->buf);
+ }
+ EP_FREEREQ(ep, req);
+ }
+}
+
+/****************************************************************************
+ * Class Driver Interfaces
+ ****************************************************************************/
+/****************************************************************************
+ * Name: usbmsc_bind
+ *
+ * Description:
+ * Invoked when the driver is bound to a USB device driver
+ *
+ ****************************************************************************/
+
+static int usbmsc_bind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct usbmsc_dev_s *priv = ((FAR struct usbmsc_driver_s*)driver)->dev;
+ FAR struct usbmsc_req_s *reqcontainer;
+ irqstate_t flags;
+ int ret = OK;
+ int i;
+
+ usbtrace(TRACE_CLASSBIND, 0);
+
+ /* Bind the structures */
+
+ priv->usbdev = dev;
+
+ /* Save the reference to our private data structure in EP0 so that it
+ * can be recovered in ep0 completion events (Unless we are part of
+ * a composite device and, in that case, the composite device owns
+ * EP0).
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ dev->ep0->priv = priv;
+#endif
+
+ /* The configured EP0 size should match the reported EP0 size. We could
+ * easily adapt to the reported EP0 size, but then we could not use the
+ * const, canned descriptors.
+ */
+
+ DEBUGASSERT(CONFIG_USBMSC_EP0MAXPACKET == dev->ep0->maxpacket);
+
+ /* Preallocate control request */
+
+ priv->ctrlreq = usbmsc_allocreq(dev->ep0, USBMSC_MXDESCLEN);
+ if (priv->ctrlreq == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_ALLOCCTRLREQ), 0);
+ ret = -ENOMEM;
+ goto errout;
+ }
+ priv->ctrlreq->callback = usbmsc_ep0incomplete;
+
+ /* Pre-allocate all endpoints... the endpoints will not be functional
+ * until the SET CONFIGURATION request is processed in usbmsc_setconfig.
+ * This is done here because there may be calls to kmalloc and the SET
+ * CONFIGURATION processing probably occurrs within interrupt handling
+ * logic where kmalloc calls will fail.
+ */
+
+ /* Pre-allocate the IN bulk endpoint */
+
+ priv->epbulkin = DEV_ALLOCEP(dev, USBMSC_EPINBULK_ADDR, true, USB_EP_ATTR_XFER_BULK);
+ if (!priv->epbulkin)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EPBULKINALLOCFAIL), 0);
+ ret = -ENODEV;
+ goto errout;
+ }
+ priv->epbulkin->priv = priv;
+
+ /* Pre-allocate the OUT bulk endpoint */
+
+ priv->epbulkout = DEV_ALLOCEP(dev, USBMSC_EPOUTBULK_ADDR, false, USB_EP_ATTR_XFER_BULK);
+ if (!priv->epbulkout)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EPBULKOUTALLOCFAIL), 0);
+ ret = -ENODEV;
+ goto errout;
+ }
+ priv->epbulkout->priv = priv;
+
+ /* Pre-allocate read requests */
+
+ for (i = 0; i < CONFIG_USBMSC_NRDREQS; i++)
+ {
+ reqcontainer = &priv->rdreqs[i];
+ reqcontainer->req = usbmsc_allocreq(priv->epbulkout, CONFIG_USBMSC_BULKOUTREQLEN);
+ if (reqcontainer->req == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_RDALLOCREQ),
+ (uint16_t)-ret);
+ ret = -ENOMEM;
+ goto errout;
+ }
+ reqcontainer->req->priv = reqcontainer;
+ reqcontainer->req->callback = usbmsc_rdcomplete;
+ }
+
+ /* Pre-allocate write request containers and put in a free list */
+
+ for (i = 0; i < CONFIG_USBMSC_NWRREQS; i++)
+ {
+ reqcontainer = &priv->wrreqs[i];
+ reqcontainer->req = usbmsc_allocreq(priv->epbulkin, CONFIG_USBMSC_BULKINREQLEN);
+ if (reqcontainer->req == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRALLOCREQ),
+ (uint16_t)-ret);
+ ret = -ENOMEM;
+ goto errout;
+ }
+ reqcontainer->req->priv = reqcontainer;
+ reqcontainer->req->callback = usbmsc_wrcomplete;
+
+ flags = irqsave();
+ sq_addlast((sq_entry_t*)reqcontainer, &priv->wrreqlist);
+ irqrestore(flags);
+ }
+
+ /* Report if we are selfpowered (unless we are part of a composite device) */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+#ifdef CONFIG_USBDEV_SELFPOWERED
+ DEV_SETSELFPOWERED(dev);
+#endif
+
+ /* And pull-up the data line for the soft connect function (unless we are
+ * part of a composite device)
+ */
+
+ DEV_CONNECT(dev);
+#endif
+ return OK;
+
+errout:
+ usbmsc_unbind(driver, dev);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_unbind
+ *
+ * Description:
+ * Invoked when the driver is unbound from a USB device driver
+ *
+ ****************************************************************************/
+
+static void usbmsc_unbind(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ FAR struct usbmsc_dev_s *priv;
+ FAR struct usbmsc_req_s *reqcontainer;
+ irqstate_t flags;
+ int i;
+
+ usbtrace(TRACE_CLASSUNBIND, 0);
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev || !dev->ep0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_UNBINDINVALIDARGS), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = ((FAR struct usbmsc_driver_s*)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EP0NOTBOUND1), 0);
+ return;
+ }
+#endif
+
+ /* The worker thread should have already been stopped by the
+ * driver un-initialize logic.
+ */
+
+ DEBUGASSERT(priv->thstate == USBMSC_STATE_TERMINATED);
+
+ /* Make sure that we are not already unbound */
+
+ if (priv != NULL)
+ {
+ /* Make sure that the endpoints have been unconfigured. If
+ * we were terminated gracefully, then the configuration should
+ * already have been reset. If not, then calling usbmsc_resetconfig
+ * should cause the endpoints to immediately terminate all
+ * transfers and return the requests to us (with result == -ESHUTDOWN)
+ */
+
+ usbmsc_resetconfig(priv);
+ up_mdelay(50);
+
+ /* Free the pre-allocated control request */
+
+ if (priv->ctrlreq != NULL)
+ {
+ usbmsc_freereq(dev->ep0, priv->ctrlreq);
+ priv->ctrlreq = NULL;
+ }
+
+ /* Free pre-allocated read requests (which should all have
+ * been returned to the free list at this time -- we don't check)
+ */
+
+ for (i = 0; i < CONFIG_USBMSC_NRDREQS; i++)
+ {
+ reqcontainer = &priv->rdreqs[i];
+ if (reqcontainer->req)
+ {
+ usbmsc_freereq(priv->epbulkout, reqcontainer->req);
+ reqcontainer->req = NULL;
+ }
+ }
+
+ /* Free the bulk OUT endpoint */
+
+ if (priv->epbulkout)
+ {
+ DEV_FREEEP(dev, priv->epbulkout);
+ priv->epbulkout = NULL;
+ }
+
+ /* Free write requests that are not in use (which should be all
+ * of them
+ */
+
+ flags = irqsave();
+ while (!sq_empty(&priv->wrreqlist))
+ {
+ reqcontainer = (struct usbmsc_req_s *)sq_remfirst(&priv->wrreqlist);
+ if (reqcontainer->req != NULL)
+ {
+ usbmsc_freereq(priv->epbulkin, reqcontainer->req);
+ }
+ }
+
+ /* Free the bulk IN endpoint */
+
+ if (priv->epbulkin)
+ {
+ DEV_FREEEP(dev, priv->epbulkin);
+ priv->epbulkin = NULL;
+ }
+
+ irqrestore(flags);
+ }
+}
+
+/****************************************************************************
+ * Name: usbmsc_setup
+ *
+ * Description:
+ * Invoked for ep0 control requests. This function probably executes
+ * in the context of an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int usbmsc_setup(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl,
+ FAR uint8_t *dataout, size_t outlen)
+{
+ FAR struct usbmsc_dev_s *priv;
+ FAR struct usbdev_req_s *ctrlreq;
+ uint16_t value;
+ uint16_t index;
+ uint16_t len;
+ int ret = -EOPNOTSUPP;
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev || !dev->ep0 || !ctrl)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_SETUPINVALIDARGS), 0);
+ return -EIO;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ usbtrace(TRACE_CLASSSETUP, ctrl->req);
+ priv = ((FAR struct usbmsc_driver_s *)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv || !priv->ctrlreq)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EP0NOTBOUND2), 0);
+ return -ENODEV;
+ }
+#endif
+ ctrlreq = priv->ctrlreq;
+
+ /* Extract the little-endian 16-bit values to host order */
+
+ value = GETUINT16(ctrl->value);
+ index = GETUINT16(ctrl->index);
+ len = GETUINT16(ctrl->len);
+
+ uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
+ ctrl->type, ctrl->req, value, index, len);
+
+ if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD)
+ {
+ /**********************************************************************
+ * Standard Requests
+ **********************************************************************/
+
+ switch (ctrl->req)
+ {
+ case USB_REQ_GETDESCRIPTOR:
+ {
+ /* The value field specifies the descriptor type in the MS byte and the
+ * descriptor index in the LS byte (order is little endian)
+ */
+
+ switch (ctrl->value[1])
+ {
+ /* If the mass storage device is used in as part of a composite
+ * device, then the device descriptor is is provided by logic
+ * in the composite device implementation.
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ case USB_DESC_TYPE_DEVICE:
+ {
+ ret = USB_SIZEOF_DEVDESC;
+ memcpy(ctrlreq->buf, usbmsc_getdevdesc(), ret);
+ }
+ break;
+#endif
+
+ /* If the mass storage device is used in as part of a composite device,
+ * then the device qualifier descriptor is provided by logic in the
+ * composite device implementation.
+ */
+
+#if !defined(CONFIG_USBMSC_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED)
+ case USB_DESC_TYPE_DEVICEQUALIFIER:
+ {
+ ret = USB_SIZEOF_QUALDESC;
+ memcpy(ctrlreq->buf, usbmsc_getqualdesc(), ret);
+ }
+ break;
+
+ case USB_DESC_TYPE_OTHERSPEEDCONFIG:
+#endif
+
+ /* If the mass storage device is used in as part of a composite device,
+ * then the configuration descriptor is provided by logic in the
+ * composite device implementation.
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ case USB_DESC_TYPE_CONFIG:
+ {
+#ifdef CONFIG_USBDEV_DUALSPEED
+ ret = usbmsc_mkcfgdesc(ctrlreq->buf, dev->speed, ctrl->value[1]);
+#else
+ ret = usbmsc_mkcfgdesc(ctrlreq->buf);
+#endif
+ }
+ break;
+#endif
+
+ /* If the mass storage device is used in as part of a composite device,
+ * then the language string descriptor is provided by logic in the
+ * composite device implementation.
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ case USB_DESC_TYPE_STRING:
+ {
+ /* index == language code. */
+
+ ret = usbmsc_mkstrdesc(ctrl->value[0], (struct usb_strdesc_s *)ctrlreq->buf);
+ }
+ break;
+#endif
+
+ default:
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_GETUNKNOWNDESC), value);
+ }
+ break;
+ }
+ }
+ break;
+
+ case USB_REQ_SETCONFIGURATION:
+ {
+ if (ctrl->type == 0)
+ {
+ /* Signal the worker thread to instantiate the new configuration */
+
+ priv->theventset |= USBMSC_EVENT_CFGCHANGE;
+ priv->thvalue = value;
+ pthread_cond_signal(&priv->cond);
+
+ /* Return here... the response will be provided later by the
+ * worker thread.
+ */
+
+ return OK;
+ }
+ }
+ break;
+
+ /* If the mass storage device is used in as part of a composite device,
+ * then the overall composite class configuration is managed by logic
+ * in the composite device implementation.
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ case USB_REQ_GETCONFIGURATION:
+ {
+ if (ctrl->type == USB_DIR_IN)
+ {
+ ctrlreq->buf[0] = priv->config;
+ ret = 1;
+ }
+ }
+ break;
+#endif
+
+ case USB_REQ_SETINTERFACE:
+ {
+ if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE)
+ {
+ if (priv->config == USBMSC_CONFIGID &&
+ index == USBMSC_INTERFACEID &&
+ value == USBMSC_ALTINTERFACEID)
+ {
+ /* Signal to instantiate the interface change */
+
+ priv->theventset |= USBMSC_EVENT_IFCHANGE;
+ pthread_cond_signal(&priv->cond);
+
+ /* Return here... the response will be provided later by the
+ * worker thread.
+ */
+
+ return OK;
+ }
+ }
+ }
+ break;
+
+ case USB_REQ_GETINTERFACE:
+ {
+ if (ctrl->type == (USB_DIR_IN|USB_REQ_RECIPIENT_INTERFACE) &&
+ priv->config == USBMSC_CONFIGIDNONE)
+ {
+ if (index != USBMSC_INTERFACEID)
+ {
+ ret = -EDOM;
+ }
+ else
+ {
+ ctrlreq->buf[0] = USBMSC_ALTINTERFACEID;
+ ret = 1;
+ }
+ }
+ }
+ break;
+
+ default:
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req);
+ break;
+ }
+ }
+ else if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS)
+ {
+ /**********************************************************************
+ * Bulk-Only Mass Storage Class Requests
+ **********************************************************************/
+
+ /* Verify that we are configured */
+
+ if (!priv->config)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_NOTCONFIGURED), 0);
+ return ret;
+ }
+
+ switch (ctrl->req)
+ {
+ case USBMSC_REQ_MSRESET: /* Reset mass storage device and interface */
+ {
+ if (ctrl->type == USBMSC_TYPE_SETUPOUT && value == 0 && len == 0)
+ {
+ /* Only one interface is supported */
+
+ if (index != USBMSC_INTERFACEID)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_MSRESETNDX), index);
+ ret = -EDOM;
+ }
+ else
+ {
+ /* Signal to stop the current operation and reinitialize state */
+
+ priv->theventset |= USBMSC_EVENT_RESET;
+ pthread_cond_signal(&priv->cond);
+
+ /* Return here... the response will be provided later by the
+ * worker thread.
+ */
+
+ return OK;
+ }
+ }
+ }
+ break;
+
+ case USBMSC_REQ_GETMAXLUN: /* Return number LUNs supported */
+ {
+ if (ctrl->type == USBMSC_TYPE_SETUPIN && value == 0 && len == 1)
+ {
+ /* Only one interface is supported */
+
+ if (index != USBMSC_INTERFACEID)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_GETMAXLUNNDX), index);
+ ret = -EDOM;
+ }
+ else
+ {
+ ctrlreq->buf[0] = priv->nluns - 1;
+ ret = 1;
+ }
+ }
+ }
+ break;
+
+ default:
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_BADREQUEST), ctrl->req);
+ break;
+ }
+ }
+ else
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_UNSUPPORTEDTYPE), ctrl->type);
+ }
+
+ /* Respond to the setup command if data was returned. On an error return
+ * value (ret < 0), the USB driver will stall EP0.
+ */
+
+ if (ret >= 0)
+ {
+ /* Configure the response */
+
+ ctrlreq->len = MIN(len, ret);
+ ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;
+
+ /* Send the response -- either directly to the USB controller or
+ * indirectly in the case where this class is a member of a composite
+ * device.
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ ret = EP_SUBMIT(dev->ep0, ctrlreq);
+#else
+ ret = composite_ep0submit(driver, dev, ctrlreq);
+#endif
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EPRESPQ), (uint16_t)-ret);
+#if 0 /* Not necessary */
+ ctrlreq->result = OK;
+ usbmsc_ep0incomplete(dev->ep0, ctrlreq);
+#endif
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_disconnect
+ *
+ * Description:
+ * Invoked after all transfers have been stopped, when the host is
+ * disconnected. This function is probably called from the context of an
+ * interrupt handler.
+ *
+ ****************************************************************************/
+
+static void usbmsc_disconnect(FAR struct usbdevclass_driver_s *driver,
+ FAR struct usbdev_s *dev)
+{
+ struct usbmsc_dev_s *priv;
+ irqstate_t flags;
+
+ usbtrace(TRACE_CLASSDISCONNECT, 0);
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !dev || !dev->ep0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_DISCONNECTINVALIDARGS), 0);
+ return;
+ }
+#endif
+
+ /* Extract reference to private data */
+
+ priv = ((FAR struct usbmsc_driver_s *)driver)->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EP0NOTBOUND3), 0);
+ return;
+ }
+#endif
+
+ /* Reset the configuration */
+
+ flags = irqsave();
+ usbmsc_resetconfig(priv);
+
+ /* Signal the worker thread */
+
+ priv->theventset |= USBMSC_EVENT_DISCONNECT;
+ pthread_cond_signal(&priv->cond);
+ irqrestore(flags);
+
+ /* Perform the soft connect function so that we will we can be
+ * re-enumerated (unless we are part of a composite device)
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ DEV_CONNECT(dev);
+#endif
+}
+
+/****************************************************************************
+ * Initialization/Un-Initialization
+ ****************************************************************************/
+/****************************************************************************
+ * Name: usbmsc_lununinitialize
+ ****************************************************************************/
+
+static void usbmsc_lununinitialize(struct usbmsc_lun_s *lun)
+{
+ /* Has a block driver has been bound to the LUN? */
+
+ if (lun->inode)
+ {
+ /* Close the block driver */
+
+ (void)close_blockdriver(lun->inode);
+ }
+
+ memset(lun, 0, sizeof(struct usbmsc_lun_s *));
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Internal Interfaces
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbmsc_setconfig
+ *
+ * Description:
+ * Set the device configuration by allocating and configuring endpoints and
+ * by allocating and queuing read and write requests.
+ *
+ ****************************************************************************/
+
+int usbmsc_setconfig(FAR struct usbmsc_dev_s *priv, uint8_t config)
+{
+ FAR struct usbmsc_req_s *privreq;
+ FAR struct usbdev_req_s *req;
+#ifdef CONFIG_USBDEV_DUALSPEED
+ FAR const struct usb_epdesc_s *epdesc;
+ bool hispeed = (priv->usbdev->speed == USB_SPEED_HIGH);
+ uint16_t bulkmxpacket;
+#endif
+ int i;
+ int ret = 0;
+
+#if CONFIG_DEBUG
+ if (priv == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_SETCONFIGINVALIDARGS), 0);
+ return -EIO;
+ }
+#endif
+
+ if (config == priv->config)
+ {
+ /* Already configured -- Do nothing */
+
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_ALREADYCONFIGURED), 0);
+ return OK;
+ }
+
+ /* Discard the previous configuration data */
+
+ usbmsc_resetconfig(priv);
+
+ /* Was this a request to simply discard the current configuration? */
+
+ if (config == USBMSC_CONFIGIDNONE)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CONFIGNONE), 0);
+ return OK;
+ }
+
+ /* We only accept one configuration */
+
+ if (config != USBMSC_CONFIGID)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CONFIGIDBAD), 0);
+ return -EINVAL;
+ }
+
+ /* Configure the IN bulk endpoint */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ bulkmxpacket = USBMSC_BULKMAXPACKET(hispeed);
+ epdesc = USBMSC_EPBULKINDESC(hispeed);
+ ret = EP_CONFIGURE(priv->epbulkin, epdesc, false);
+#else
+ ret = EP_CONFIGURE(priv->epbulkin,
+ usbmsc_getepdesc(USBMSC_EPFSBULKIN), false);
+#endif
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EPBULKINCONFIGFAIL), 0);
+ goto errout;
+ }
+
+ priv->epbulkin->priv = priv;
+
+ /* Configure the OUT bulk endpoint */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ epdesc = USBMSC_EPBULKOUTDESC(hispeed);
+ ret = EP_CONFIGURE(priv->epbulkout, epdesc, true);
+#else
+ ret = EP_CONFIGURE(priv->epbulkout,
+ usbmsc_getepdesc(USBMSC_EPFSBULKOUT), true);
+#endif
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EPBULKOUTCONFIGFAIL), 0);
+ goto errout;
+ }
+
+ priv->epbulkout->priv = priv;
+
+ /* Queue read requests in the bulk OUT endpoint */
+
+ for (i = 0; i < CONFIG_USBMSC_NRDREQS; i++)
+ {
+ privreq = &priv->rdreqs[i];
+ req = privreq->req;
+ req->len = CONFIG_USBMSC_BULKOUTREQLEN;
+ req->priv = privreq;
+ req->callback = usbmsc_rdcomplete;
+ ret = EP_SUBMIT(priv->epbulkout, req);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_RDSUBMIT), (uint16_t)-ret);
+ goto errout;
+ }
+ }
+
+ priv->config = config;
+ return OK;
+
+errout:
+ usbmsc_resetconfig(priv);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_resetconfig
+ *
+ * Description:
+ * Mark the device as not configured and disable all endpoints.
+ *
+ ****************************************************************************/
+
+void usbmsc_resetconfig(FAR struct usbmsc_dev_s *priv)
+{
+ /* Are we configured? */
+
+ if (priv->config != USBMSC_CONFIGIDNONE)
+ {
+ /* Yes.. but not anymore */
+
+ priv->config = USBMSC_CONFIGIDNONE;
+
+ /* Disable endpoints. This should force completion of all pending
+ * transfers.
+ */
+
+ EP_DISABLE(priv->epbulkin);
+ EP_DISABLE(priv->epbulkout);
+ }
+}
+
+/****************************************************************************
+ * Name: usbmsc_wrcomplete
+ *
+ * Description:
+ * Handle completion of write request. This function probably executes
+ * in the context of an interrupt handler.
+ *
+ ****************************************************************************/
+
+void usbmsc_wrcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
+{
+ FAR struct usbmsc_dev_s *priv;
+ FAR struct usbmsc_req_s *privreq;
+ irqstate_t flags;
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (!ep || !ep->priv || !req || !req->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRCOMPLETEINVALIDARGS), 0);
+ return;
+ }
+#endif
+
+ /* Extract references to private data */
+
+ priv = (FAR struct usbmsc_dev_s*)ep->priv;
+ privreq = (FAR struct usbmsc_req_s *)req->priv;
+
+ /* Return the write request to the free list */
+
+ flags = irqsave();
+ sq_addlast((sq_entry_t*)privreq, &priv->wrreqlist);
+ irqrestore(flags);
+
+ /* Process the received data unless this is some unusual condition */
+
+ switch (req->result)
+ {
+ case OK: /* Normal completion */
+ usbtrace(TRACE_CLASSWRCOMPLETE, req->xfrd);
+ break;
+
+ case -ESHUTDOWN: /* Disconnection */
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRSHUTDOWN), 0);
+ break;
+
+ default: /* Some other error occurred */
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRUNEXPECTED),
+ (uint16_t)-req->result);
+ break;
+ };
+
+ /* Inform the worker thread that a write request has been returned */
+
+ priv->theventset |= USBMSC_EVENT_WRCOMPLETE;
+ pthread_cond_signal(&priv->cond);
+}
+
+/****************************************************************************
+ * Name: usbmsc_rdcomplete
+ *
+ * Description:
+ * Handle completion of read request on the bulk OUT endpoint. This
+ * is handled like the receipt of serial data on the "UART"
+ *
+ ****************************************************************************/
+
+void usbmsc_rdcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
+{
+ FAR struct usbmsc_dev_s *priv;
+ FAR struct usbmsc_req_s *privreq;
+ irqstate_t flags;
+ int ret;
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (!ep || !ep->priv || !req || !req->priv)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_RDCOMPLETEINVALIDARGS), 0);
+ return;
+ }
+#endif
+
+ /* Extract references to private data */
+
+ priv = (FAR struct usbmsc_dev_s*)ep->priv;
+ privreq = (FAR struct usbmsc_req_s *)req->priv;
+
+ /* Process the received data unless this is some unusual condition */
+
+ switch (req->result)
+ {
+ case 0: /* Normal completion */
+ {
+ usbtrace(TRACE_CLASSRDCOMPLETE, req->xfrd);
+
+ /* Add the filled read request from the rdreqlist */
+
+ flags = irqsave();
+ sq_addlast((sq_entry_t*)privreq, &priv->rdreqlist);
+ irqrestore(flags);
+
+ /* Signal the worker thread that there is received data to be processed */
+
+ priv->theventset |= USBMSC_EVENT_RDCOMPLETE;
+ pthread_cond_signal(&priv->cond);
+ }
+ break;
+
+ case -ESHUTDOWN: /* Disconnection */
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_RDSHUTDOWN), 0);
+
+ /* Drop the read request... it will be cleaned up later */
+ }
+ break;
+
+ default: /* Some other error occurred */
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_RDUNEXPECTED),
+ (uint16_t)-req->result);
+
+ /* Return the read request to the bulk out endpoint for re-filling */
+
+ req = privreq->req;
+ req->priv = privreq;
+ req->callback = usbmsc_rdcomplete;
+
+ ret = EP_SUBMIT(priv->epbulkout, req);
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_RDCOMPLETERDSUBMIT),
+ (uint16_t)-ret);
+ }
+ }
+ break;
+ }
+}
+
+/****************************************************************************
+ * Name: usbmsc_deferredresponse
+ *
+ * Description:
+ * Some EP0 setup request cannot be responded to immediately becuase they
+ * require some asynchronous action from the SCSI worker thread. This
+ * function is provided for the SCSI thread to make that deferred response.
+ * The specific requests that require this deferred response are:
+ *
+ * 1. USB_REQ_SETCONFIGURATION,
+ * 2. USB_REQ_SETINTERFACE, or
+ * 3. USBMSC_REQ_MSRESET
+ *
+ * In all cases, the success reponse is a zero-length packet; the failure
+ * response is an EP0 stall.
+ *
+ * Input parameters:
+ * priv - Private state structure for this USB storage instance
+ * stall - true is the action failed and a stall is required
+ *
+ ****************************************************************************/
+
+void usbmsc_deferredresponse(FAR struct usbmsc_dev_s *priv, bool failed)
+{
+ FAR struct usbdev_s *dev;
+ FAR struct usbdev_req_s *ctrlreq;
+ int ret;
+
+#ifdef CONFIG_DEBUG
+ if (!priv || !priv->usbdev || !priv->ctrlreq)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_DEFERREDRESPINVALIDARGS), 0);
+ return;
+ }
+#endif
+
+ dev = priv->usbdev;
+ ctrlreq = priv->ctrlreq;
+
+ /* If no error occurs, respond to the deferred setup command with a null
+ * packet.
+ */
+
+ if (!failed)
+ {
+ ctrlreq->len = 0;
+ ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;
+ ret = EP_SUBMIT(dev->ep0, ctrlreq);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_DEFERREDRESPSUBMIT),
+ (uint16_t)-ret);
+#if 0 /* Not necessary */
+ ctrlreq->result = OK;
+ usbmsc_ep0incomplete(dev->ep0, ctrlreq);
+#endif
+ }
+ }
+ else
+ {
+ /* On a failure, the USB driver will stall. */
+
+ usbtrace(TRACE_DEVERROR(USBMSC_TRACEERR_DEFERREDRESPSTALLED), 0);
+ EP_STALL(dev->ep0);
+ }
+}
+
+/****************************************************************************
+ * User Interfaces
+ ****************************************************************************/
+/****************************************************************************
+ * Name: usbmsc_configure
+ *
+ * Description:
+ * One-time initialization of the USB storage driver. The initialization
+ * sequence is as follows:
+ *
+ * 1. Call usbmsc_configure to perform one-time initialization specifying
+ * the number of luns.
+ * 2. Call usbmsc_bindlun to configure each supported LUN
+ * 3. Call usbmsc_exportluns when all LUNs are configured
+ *
+ * Input Parameters:
+ * nluns - the number of LUNs that will be registered
+ * handle - Location to return a handle that is used in other API calls.
+ *
+ * Returned Value:
+ * 0 on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int usbmsc_configure(unsigned int nluns, void **handle)
+{
+ FAR struct usbmsc_alloc_s *alloc;
+ FAR struct usbmsc_dev_s *priv;
+ FAR struct usbmsc_driver_s *drvr;
+ int ret;
+
+#ifdef CONFIG_DEBUG
+ if (nluns > 15)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_TOOMANYLUNS), 0);
+ return -EDOM;
+ }
+#endif
+
+ /* Allocate the structures needed */
+
+ alloc = (FAR struct usbmsc_alloc_s*)kmalloc(sizeof(struct usbmsc_alloc_s));
+ if (!alloc)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_ALLOCDEVSTRUCT), 0);
+ return -ENOMEM;
+ }
+
+ /* Initialize the USB storage driver structure */
+
+ priv = &alloc->dev;
+ memset(priv, 0, sizeof(struct usbmsc_dev_s));
+
+ pthread_mutex_init(&priv->mutex, NULL);
+ pthread_cond_init(&priv->cond, NULL);
+ sq_init(&priv->wrreqlist);
+
+ priv->nluns = nluns;
+
+ /* Allocate the LUN table */
+
+ priv->luntab = (struct usbmsc_lun_s*)kmalloc(priv->nluns*sizeof(struct usbmsc_lun_s));
+ if (!priv->luntab)
+ {
+ ret = -ENOMEM;
+ goto errout;
+ }
+ memset(priv->luntab, 0, priv->nluns * sizeof(struct usbmsc_lun_s));
+
+ /* Initialize the USB class driver structure */
+
+ drvr = &alloc->drvr;
+#ifdef CONFIG_USBDEV_DUALSPEED
+ drvr->drvr.speed = USB_SPEED_HIGH;
+#else
+ drvr->drvr.speed = USB_SPEED_FULL;
+#endif
+ drvr->drvr.ops = &g_driverops;
+ drvr->dev = priv;
+
+ /* Return the handle and success */
+
+ *handle = (FAR void*)alloc;
+ return OK;
+
+errout:
+ usbmsc_uninitialize(alloc);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_bindlun
+ *
+ * Description:
+ * Bind the block driver specified by drvrpath to a USB storage LUN.
+ *
+ * Input Parameters:
+ * handle - The handle returned by a previous call to usbmsc_configure().
+ * drvrpath - the full path to the block driver
+ * startsector - A sector offset into the block driver to the start of the
+ * partition on drvrpath (0 if no partitions)
+ * nsectors - The number of sectors in the partition (if 0, all sectors
+ * to the end of the media will be exported).
+ * lunno - the LUN to bind to
+ *
+ * Returned Value:
+ * 0 on success; a negated errno on failure.
+ *
+ ****************************************************************************/
+
+int usbmsc_bindlun(FAR void *handle, FAR const char *drvrpath,
+ unsigned int lunno, off_t startsector, size_t nsectors,
+ bool readonly)
+{
+ FAR struct usbmsc_alloc_s *alloc = (FAR struct usbmsc_alloc_s *)handle;
+ FAR struct usbmsc_dev_s *priv;
+ FAR struct usbmsc_lun_s *lun;
+ FAR struct inode *inode;
+ struct geometry geo;
+ int ret;
+
+#ifdef CONFIG_DEBUG
+ if (!alloc || !drvrpath || startsector < 0 || nsectors < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_BINLUNINVALIDARGS1), 0);
+ return -EINVAL;
+ }
+#endif
+
+ priv = &alloc->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv->luntab)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_INTERNALCONFUSION1), 0);
+ return -EIO;
+ }
+
+ if (lunno > priv->nluns)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_BINDLUNINVALIDARGS2), 0);
+ return -EINVAL;
+ }
+#endif
+
+ lun = &priv->luntab[lunno];
+
+#ifdef CONFIG_DEBUG
+ if (lun->inode != NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_LUNALREADYBOUND), 0);
+ return -EBUSY;
+ }
+#endif
+
+ /* Open the block driver */
+
+ ret = open_blockdriver(drvrpath, 0, &inode);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_BLKDRVEOPEN), 0);
+ return ret;
+ }
+
+ /* Get the drive geometry */
+
+ if (!inode || !inode->u.i_bops || !inode->u.i_bops->geometry ||
+ inode->u.i_bops->geometry(inode, &geo) != OK || !geo.geo_available)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_NOGEOMETRY), 0);
+ return -ENODEV;
+ }
+
+ /* Verify that the partition parameters are valid */
+
+ if (startsector >= geo.geo_nsectors)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_BINDLUNINVALIDARGS3), 0);
+ return -EDOM;
+ }
+ else if (nsectors == 0)
+ {
+ nsectors = geo.geo_nsectors - startsector;
+ }
+ else if (startsector + nsectors >= geo.geo_nsectors)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_BINDLUNINVALIDARGS4), 0);
+ return -EDOM;
+ }
+
+ /* Initialize the LUN structure */
+
+ memset(lun, 0, sizeof(struct usbmsc_lun_s *));
+
+ /* Allocate an I/O buffer big enough to hold one hardware sector. SCSI commands
+ * are processed one at a time so all LUNs may share a single I/O buffer. The
+ * I/O buffer will be allocated so that is it as large as the largest block
+ * device sector size
+ */
+
+ if (!priv->iobuffer)
+ {
+ priv->iobuffer = (uint8_t*)kmalloc(geo.geo_sectorsize);
+ if (!priv->iobuffer)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_ALLOCIOBUFFER), geo.geo_sectorsize);
+ return -ENOMEM;
+ }
+ priv->iosize = geo.geo_sectorsize;
+ }
+ else if (priv->iosize < geo.geo_sectorsize)
+ {
+ void *tmp;
+ tmp = (uint8_t*)realloc(priv->iobuffer, geo.geo_sectorsize);
+ if (!tmp)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_REALLOCIOBUFFER), geo.geo_sectorsize);
+ return -ENOMEM;
+ }
+
+ priv->iobuffer = (uint8_t*)tmp;
+ priv->iosize = geo.geo_sectorsize;
+ }
+
+ lun->inode = inode;
+ lun->startsector = startsector;
+ lun->nsectors = nsectors;
+ lun->sectorsize = geo.geo_sectorsize;
+
+ /* If the driver does not support the write method, then this is read-only */
+
+ if (!inode->u.i_bops->write)
+ {
+ lun->readonly = true;
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbmsc_unbindlun
+ *
+ * Description:
+ * Un-bind the block driver for the specified LUN
+ *
+ * Input Parameters:
+ * handle - The handle returned by a previous call to usbmsc_configure().
+ * lun - the LUN to unbind from
+ *
+ * Returned Value:
+ * 0 on success; a negated errno on failure.
+ *
+ ****************************************************************************/
+
+int usbmsc_unbindlun(FAR void *handle, unsigned int lunno)
+{
+ FAR struct usbmsc_alloc_s *alloc = (FAR struct usbmsc_alloc_s *)handle;
+ FAR struct usbmsc_dev_s *priv;
+ FAR struct usbmsc_lun_s *lun;
+ int ret;
+
+#ifdef CONFIG_DEBUG
+ if (!alloc)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_UNBINDLUNINVALIDARGS1), 0);
+ return -EINVAL;
+ }
+#endif
+
+ priv = &alloc->dev;
+
+#ifdef CONFIG_DEBUG
+ if (!priv->luntab)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_INTERNALCONFUSION2), 0);
+ return -EIO;
+ }
+
+ if (lunno > priv->nluns)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_UNBINDLUNINVALIDARGS2), 0);
+ return -EINVAL;
+ }
+#endif
+
+ lun = &priv->luntab[lunno];
+ pthread_mutex_lock(&priv->mutex);
+
+#ifdef CONFIG_DEBUG
+ if (lun->inode == NULL)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_LUNNOTBOUND), 0);
+ ret = -EBUSY;
+ }
+ else
+#endif
+ {
+ /* Close the block driver */
+
+ usbmsc_lununinitialize(lun);
+ ret = OK;
+ }
+
+ pthread_mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_exportluns
+ *
+ * Description:
+ * After all of the LUNs have been bound, this function may be called
+ * in order to export those LUNs in the USB storage device.
+ *
+ * Input Parameters:
+ * handle - The handle returned by a previous call to usbmsc_configure().
+ *
+ * Returned Value:
+ * 0 on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBMSC_COMPOSITE
+static
+#endif
+int usbmsc_exportluns(FAR void *handle)
+{
+ FAR struct usbmsc_alloc_s *alloc = (FAR struct usbmsc_alloc_s *)handle;
+ FAR struct usbmsc_dev_s *priv;
+ FAR struct usbmsc_driver_s *drvr;
+ irqstate_t flags;
+#ifdef SDCC
+ pthread_attr_t attr;
+#endif
+ int ret;
+
+#ifdef CONFIG_DEBUG
+ if (!alloc)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EXPORTLUNSINVALIDARGS), 0);
+ return -ENXIO;
+ }
+#endif
+
+ priv = &alloc->dev;
+ drvr = &alloc->drvr;
+
+ /* Start the worker thread */
+
+ pthread_mutex_lock(&priv->mutex);
+ priv->thstate = USBMSC_STATE_NOTSTARTED;
+ priv->theventset = USBMSC_EVENT_NOEVENTS;
+
+#ifdef SDCC
+ (void)pthread_attr_init(&attr);
+ ret = pthread_create(&priv->thread, &attr, usbmsc_workerthread, (pthread_addr_t)priv);
+#else
+ ret = pthread_create(&priv->thread, NULL, usbmsc_workerthread, (pthread_addr_t)priv);
+#endif
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_THREADCREATE), (uint16_t)-ret);
+ goto errout_with_mutex;
+ }
+
+ /* Register the USB storage class driver (unless we are part of a composite device) */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ ret = usbdev_register(&drvr->drvr);
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_DEVREGISTER), (uint16_t)-ret);
+ goto errout_with_mutex;
+ }
+#endif
+
+ /* Signal to start the thread */
+
+ flags = irqsave();
+ priv->theventset |= USBMSC_EVENT_READY;
+ pthread_cond_signal(&priv->cond);
+ irqrestore(flags);
+
+errout_with_mutex:
+ pthread_mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_classobject
+ *
+ * Description:
+ * Register USB mass storage device and return the class object.
+ *
+ * Input Parameters:
+ * classdev - The location to return the CDC serial class' device
+ * instance.
+ *
+ * Returned Value:
+ * 0 on success; a negated errno on failure
+
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBMSC_COMPOSITE
+int usbmsc_classobject(FAR void *handle,
+ FAR struct usbdevclass_driver_s **classdev)
+{
+ FAR struct usbmsc_alloc_s *alloc = (FAR struct usbmsc_alloc_s *)handle;
+ int ret;
+
+ DEBUGASSERT(handle && classdev);
+
+ /* Export the LUNs as with the "standalone" USB mass storage driver, but
+ * don't register the class instance with the USB device infrastructure.
+ */
+
+ ret = usbmsc_exportluns(handle);
+ if (ret == OK)
+ {
+ /* On sucess, return an (typed) instance of the class instance */
+
+ *classdev = &alloc->drvr.drvr;
+ }
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: usbmsc_uninitialize
+ *
+ * Description:
+ * Un-initialize the USB storage class driver
+ *
+ * Input Parameters:
+ * handle - The handle returned by a previous call to usbmsc_configure().
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void usbmsc_uninitialize(FAR void *handle)
+{
+ FAR struct usbmsc_alloc_s *alloc = (FAR struct usbmsc_alloc_s *)handle;
+ FAR struct usbmsc_dev_s *priv;
+ irqstate_t flags;
+#ifdef SDCC
+ pthread_addr_t result1, result2;
+ pthread_attr_t attr;
+#endif
+ void *value;
+ int i;
+
+#ifdef CONFIG_DEBUG
+ if (!handle)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_UNINITIALIZEINVALIDARGS), 0);
+ return;
+ }
+#endif
+ priv = &alloc->dev;
+
+ /* If the thread hasn't already exitted, tell it to exit now */
+
+ if (priv->thstate != USBMSC_STATE_NOTSTARTED)
+ {
+ /* The thread was started.. Is it still running? */
+
+ pthread_mutex_lock(&priv->mutex);
+ if (priv->thstate != USBMSC_STATE_TERMINATED)
+ {
+ /* Yes.. Ask the thread to stop */
+
+ flags = irqsave();
+ priv->theventset |= USBMSC_EVENT_TERMINATEREQUEST;
+ pthread_cond_signal(&priv->cond);
+ irqrestore(flags);
+ }
+ pthread_mutex_unlock(&priv->mutex);
+
+ /* Wait for the thread to exit. This is necessary even if the
+ * thread has already exitted in order to collect the join
+ * garbage
+ */
+
+ (void)pthread_join(priv->thread, &value);
+ }
+ priv->thread = 0;
+
+ /* Unregister the driver (unless we are a part of a composite device */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ usbdev_unregister(&alloc->drvr.drvr);
+#endif
+
+ /* Uninitialize and release the LUNs */
+
+ for (i = 0; i < priv->nluns; ++i)
+ {
+ usbmsc_lununinitialize(&priv->luntab[i]);
+ }
+ kfree(priv->luntab);
+
+ /* Release the I/O buffer */
+
+ if (priv->iobuffer)
+ {
+ kfree(priv->iobuffer);
+ }
+
+ /* Uninitialize and release the driver structure */
+
+ pthread_mutex_destroy(&priv->mutex);
+ pthread_cond_destroy(&priv->cond);
+
+ kfree(priv);
+}
diff --git a/nuttx/drivers/usbdev/usbmsc.h b/nuttx/drivers/usbdev/usbmsc.h
new file mode 100644
index 000000000..6a5530d9d
--- /dev/null
+++ b/nuttx/drivers/usbdev/usbmsc.h
@@ -0,0 +1,694 @@
+/****************************************************************************
+ * drivers/usbdev/usbmsc.h
+ *
+ * Copyright (C) 2008-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Mass storage class device. Bulk-only with SCSI subclass.
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_USBDEV_USBMSC_H
+#define __DRIVERS_USBDEV_USBMSC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <pthread.h>
+#include <queue.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/usb/storage.h>
+#include <nuttx/usb/usbdev.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+/* If the USB mass storage device is configured as part of a composite device
+ * then both CONFIG_USBDEV_COMPOSITE and CONFIG_USBMSC_COMPOSITE must be
+ * defined.
+ */
+
+#ifndef CONFIG_USBDEV_COMPOSITE
+# undef CONFIG_USBMSC_COMPOSITE
+#endif
+
+#if defined(CONFIG_USBMSC_COMPOSITE) && !defined(CONFIG_USBMSC_STRBASE)
+# define CONFIG_USBMSC_STRBASE (4)
+#endif
+
+/* Interface IDs. If the mass storage driver is built as a component of a
+ * composite device, then the interface IDs may need to be offset.
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+# undef CONFIG_USBMSC_IFNOBASE
+# define CONFIG_USBMSC_IFNOBASE 0
+#endif
+
+#ifndef CONFIG_USBMSC_IFNOBASE
+# define CONFIG_USBMSC_IFNOBASE 0
+#endif
+
+/* Number of requests in the write queue */
+
+#ifndef CONFIG_USBMSC_NWRREQS
+# define CONFIG_USBMSC_NWRREQS 4
+#endif
+
+/* Number of requests in the read queue */
+
+#ifndef CONFIG_USBMSC_NRDREQS
+# define CONFIG_USBMSC_NRDREQS 4
+#endif
+
+/* Logical endpoint numbers / max packet sizes */
+
+#ifndef CONFIG_USBMSC_EPBULKOUT
+# warning "EPBULKOUT not defined in the configuration"
+# define CONFIG_USBMSC_EPBULKOUT 2
+#endif
+
+#ifndef CONFIG_USBMSC_EPBULKIN
+# warning "EPBULKIN not defined in the configuration"
+# define CONFIG_USBMSC_EPBULKIN 3
+#endif
+
+/* Packet and request buffer sizes */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+# ifndef CONFIG_USBMSC_EP0MAXPACKET
+# define CONFIG_USBMSC_EP0MAXPACKET 64
+# endif
+#endif
+
+#ifndef CONFIG_USBMSC_BULKINREQLEN
+# ifdef CONFIG_USBDEV_DUALSPEED
+# define CONFIG_USBMSC_BULKINREQLEN 512
+# else
+# define CONFIG_USBMSC_BULKINREQLEN 64
+# endif
+#else
+# ifdef CONFIG_USBDEV_DUALSPEED
+# if CONFIG_USBMSC_BULKINREQLEN < 512
+# warning "Bulk in buffer size smaller than max packet"
+# undef CONFIG_USBMSC_BULKINREQLEN
+# define CONFIG_USBMSC_BULKINREQLEN 512
+# endif
+# else
+# if CONFIG_USBMSC_BULKINREQLEN < 64
+# warning "Bulk in buffer size smaller than max packet"
+# undef CONFIG_USBMSC_BULKINREQLEN
+# define CONFIG_USBMSC_BULKINREQLEN 64
+# endif
+# endif
+#endif
+
+#ifndef CONFIG_USBMSC_BULKOUTREQLEN
+# ifdef CONFIG_USBDEV_DUALSPEED
+# define CONFIG_USBMSC_BULKOUTREQLEN 512
+# else
+# define CONFIG_USBMSC_BULKOUTREQLEN 64
+# endif
+#else
+# ifdef CONFIG_USBDEV_DUALSPEED
+# if CONFIG_USBMSC_BULKOUTREQLEN < 512
+# warning "Bulk in buffer size smaller than max packet"
+# undef CONFIG_USBMSC_BULKOUTREQLEN
+# define CONFIG_USBMSC_BULKOUTREQLEN 512
+# endif
+# else
+# if CONFIG_USBMSC_BULKOUTREQLEN < 64
+# warning "Bulk in buffer size smaller than max packet"
+# undef CONFIG_USBMSC_BULKOUTREQLEN
+# define CONFIG_USBMSC_BULKOUTREQLEN 64
+# endif
+# endif
+#endif
+
+/* Vendor and product IDs and strings */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+# ifndef CONFIG_USBMSC_VENDORID
+# warning "CONFIG_USBMSC_VENDORID not defined"
+# define CONFIG_USBMSC_VENDORID 0x584e
+# endif
+
+# ifndef CONFIG_USBMSC_PRODUCTID
+# warning "CONFIG_USBMSC_PRODUCTID not defined"
+# define CONFIG_USBMSC_PRODUCTID 0x5342
+# endif
+
+# ifndef CONFIG_USBMSC_VERSIONNO
+# define CONFIG_USBMSC_VERSIONNO (0x0399)
+# endif
+
+# ifndef CONFIG_USBMSC_VENDORSTR
+# warning "No Vendor string specified"
+# define CONFIG_USBMSC_VENDORSTR "NuttX"
+# endif
+
+# ifndef CONFIG_USBMSC_PRODUCTSTR
+# warning "No Product string specified"
+# define CONFIG_USBMSC_PRODUCTSTR "USBdev Storage"
+# endif
+
+# undef CONFIG_USBMSC_SERIALSTR
+# define CONFIG_USBMSC_SERIALSTR "0101"
+#endif
+
+#undef CONFIG_USBMSC_CONFIGSTR
+#define CONFIG_USBMSC_CONFIGSTR "Bulk"
+
+/* Debug -- must be consistent with include/debug.h */
+
+#ifdef CONFIG_CPP_HAVE_VARARGS
+# ifdef CONFIG_DEBUG
+# ifdef CONFIG_ARCH_LOWPUTC
+# define dbgprintf(format, arg...) lib_lowprintf(format, ##arg)
+# else
+# define dbgprintf(format, arg...) lib_rawprintf(format, ##arg)
+# endif
+# else
+# define dbgprintf(x...)
+# endif
+#else
+# ifdef CONFIG_DEBUG
+# ifdef CONFIG_ARCH_LOWPUTC
+# define dbgprintf lib_lowprintf
+# else
+# define dbgprintf lib_rawprintf
+# endif
+# else
+# define dbgprintf (void)
+# endif
+#endif
+
+/* Packet and request buffer sizes */
+
+#ifndef CONFIG_USBMSC_EP0MAXPACKET
+# define CONFIG_USBMSC_EP0MAXPACKET 64
+#endif
+
+/* USB Controller */
+
+#ifndef CONFIG_USBDEV_SELFPOWERED
+# define SELFPOWERED USB_CONFIG_ATT_SELFPOWER
+#else
+# define SELFPOWERED (0)
+#endif
+
+#ifndef CONFIG_USBDEV_REMOTEWAKEUP
+# define REMOTEWAKEUP USB_CONFIG_ATTR_WAKEUP
+#else
+# define REMOTEWAKEUP (0)
+#endif
+
+#ifndef CONFIG_USBDEV_MAXPOWER
+# define CONFIG_USBDEV_MAXPOWER 100
+#endif
+
+/* Current state of the worker thread */
+
+#define USBMSC_STATE_NOTSTARTED (0) /* Thread has not yet been started */
+#define USBMSC_STATE_STARTED (1) /* Started, but is not yet initialized */
+#define USBMSC_STATE_IDLE (2) /* Started and waiting for commands */
+#define USBMSC_STATE_CMDPARSE (3) /* Processing a received command */
+#define USBMSC_STATE_CMDREAD (4) /* Processing a SCSI read command */
+#define USBMSC_STATE_CMDWRITE (5) /* Processing a SCSI write command */
+#define USBMSC_STATE_CMDFINISH (6) /* Finish command processing */
+#define USBMSC_STATE_CMDSTATUS (7) /* Processing the final status of the command */
+#define USBMSC_STATE_TERMINATED (8) /* Thread has exitted */
+
+/* Event communicated to worker thread */
+
+#define USBMSC_EVENT_NOEVENTS (0) /* There are no outstanding events */
+#define USBMSC_EVENT_READY (1 << 0) /* Initialization is complete */
+#define USBMSC_EVENT_RDCOMPLETE (1 << 1) /* A read has completed there is data to be processed */
+#define USBMSC_EVENT_WRCOMPLETE (1 << 2) /* A write has completed and a request is available */
+#define USBMSC_EVENT_TERMINATEREQUEST (1 << 3) /* Shutdown requested */
+#define USBMSC_EVENT_DISCONNECT (1 << 4) /* USB disconnect received */
+#define USBMSC_EVENT_RESET (1 << 5) /* USB storage setup reset received */
+#define USBMSC_EVENT_CFGCHANGE (1 << 6) /* USB setup configuration change received */
+#define USBMSC_EVENT_IFCHANGE (1 << 7) /* USB setup interface change received */
+#define USBMSC_EVENT_ABORTBULKOUT (1 << 8) /* SCSI receive failure */
+
+/* SCSI command flags (passed to usbmsc_setupcmd()) */
+
+#define USBMSC_FLAGS_DIRMASK (0x03) /* Bits 0-1: Data direction */
+#define USBMSC_FLAGS_DIRNONE (0x00) /* No data to send */
+#define USBMSC_FLAGS_DIRHOST2DEVICE (0x01) /* Host-to-device */
+#define USBMSC_FLAGS_DIRDEVICE2HOST (0x02) /* Device-to-host */
+#define USBMSC_FLAGS_BLOCKXFR (0x04) /* Bit 2: Command is a block transfer request */
+#define USBMSC_FLAGS_LUNNOTNEEDED (0x08) /* Bit 3: Command does not require a valid LUN */
+#define USBMSC_FLAGS_UACOKAY (0x10) /* Bit 4: Command OK if unit attention condition */
+#define USBMSC_FLAGS_RETAINSENSEDATA (0x20) /* Bit 5: Do not clear sense data */
+
+/* Descriptors **************************************************************/
+
+/* Big enough to hold our biggest descriptor */
+
+#define USBMSC_MXDESCLEN (64)
+
+/* String language */
+
+#define USBMSC_STR_LANGUAGE (0x0409) /* en-us */
+
+/* Descriptor strings */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+# define USBMSC_MANUFACTURERSTRID (1)
+# define USBMSC_PRODUCTSTRID (2)
+# define USBMSC_SERIALSTRID (3)
+# define USBMSC_CONFIGSTRID (4)
+# define USBMSC_INTERFACESTRID USBMSC_CONFIGSTRID
+
+# undef CONFIG_USBMSC_STRBASE
+# define CONFIG_USBMSC_STRBASE (0)
+#else
+# define USBMSC_INTERFACESTRID (CONFIG_USBMSC_STRBASE+1)
+#endif
+
+#define USBMSC_LASTSTRID USBMSC_INTERFACESTRID
+#define USBMSC_NSTRIDS (USBMSC_LASTSTRID - CONFIG_USBMSC_STRBASE)
+
+#define USBMSC_NCONFIGS (1) /* Number of configurations supported */
+
+/* Configuration Descriptor */
+
+#define USBMSC_NINTERFACES (1) /* Number of interfaces in the configuration */
+#define USBMSC_INTERFACEID (CONFIG_USBMSC_IFNOBASE+0)
+#define USBMSC_ALTINTERFACEID (0)
+
+#define USBMSC_CONFIGIDNONE (0) /* Config ID means to return to address mode */
+#define USBMSC_CONFIGID (1) /* The only supported configuration ID */
+
+/* Interface description */
+
+#define USBMSC_NENDPOINTS (2) /* Number of endpoints in the interface */
+
+/* Endpoint configuration */
+
+#define USBMSC_EPOUTBULK_ADDR (CONFIG_USBMSC_EPBULKOUT)
+#define USBMSC_EPOUTBULK_ATTR (USB_EP_ATTR_XFER_BULK)
+
+#define USBMSC_EPINBULK_ADDR (USB_DIR_IN|CONFIG_USBMSC_EPBULKIN)
+#define USBMSC_EPINBULK_ATTR (USB_EP_ATTR_XFER_BULK)
+
+#define USBMSC_HSBULKMAXPACKET (512)
+#define USBMSC_HSBULKMXPKTSHIFT (9)
+#define USBMSC_HSBULKMXPKTMASK (0x000001ff)
+#define USBMSC_FSBULKMAXPACKET (64)
+#define USBMSC_FSBULKMXPKTSHIFT (6)
+#define USBMSC_FSBULKMXPKTMASK (0x0000003f)
+
+/* Macros for dual speed vs. full speed only operation */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+# define USBMSC_EPBULKINDESC(hs) \
+ usbmsc_getepdesc((hs) ? USBMSC_EPHSBULKIN : USBMSC_EPFSBULKIN)
+# define USBMSC_EPBULKOUTDESC(hs) \
+ usbmsc_getepdesc((hs) ? USBMSC_EPHSBULKOUT : USBMSC_EPFSBULKOUT)
+# define USBMSC_BULKMAXPACKET(hs) \
+ ((hs) ? USBMSC_HSBULKMAXPACKET : USBMSC_FSBULKMAXPACKET)
+# define USBMSC_BULKMXPKTSHIFT(d) \
+ (((d)->speed==USB_SPEED_HIGH) ? USBMSC_HSBULKMXPKTSHIFT : USBMSC_FSBULKMXPKTSHIFT)
+# define USBMSC_BULKMXPKTMASK(d) \
+ (((d)->speed==USB_SPEED_HIGH) ? USBMSC_HSBULKMXPKTMASK : USBMSC_FSBULKMXPKTMASK)
+#else
+# define USBMSC_EPBULKINDESC(d) usbmsc_getepdesc(USBMSC_EPFSBULKIN)
+# define USBMSC_EPBULKOUTDESC(d) usbmsc_getepdesc(USBMSC_EPFSBULKOUT)
+# define USBMSC_BULKMAXPACKET(hs) USBMSC_FSBULKMAXPACKET
+# define USBMSC_BULKMXPKTSHIFT(d) USBMSC_FSBULKMXPKTSHIFT
+# define USBMSC_BULKMXPKTMASK(d) USBMSC_FSBULKMXPKTMASK
+#endif
+
+/* Configuration descriptor size */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+
+/* Number of individual descriptors in the configuration descriptor:
+ * (1) Configuration descriptor + (1) interface descriptor + (2) interface
+ * descriptors.
+ */
+
+# define USBMSC_CFGGROUP_SIZE (4)
+
+/* The size of the config descriptor: (9 + 9 + 2*7) = 32 */
+
+# define SIZEOF_USBMSC_CFGDESC \
+ (USB_SIZEOF_CFGDESC + USB_SIZEOF_IFDESC + USBMSC_NENDPOINTS * USB_SIZEOF_EPDESC)
+
+#else
+
+/* Number of individual descriptors in the configuration descriptor:
+ * (1) interface descriptor + (2) interface descriptors.
+ */
+
+# define USBMSC_CFGGROUP_SIZE (3)
+
+/* The size of the config descriptor: (9 + 2*7) = 23 */
+
+# define SIZEOF_USBMSC_CFGDESC \
+ (USB_SIZEOF_IFDESC + USBMSC_NENDPOINTS * USB_SIZEOF_EPDESC)
+
+#endif
+
+/* Block driver helpers *****************************************************/
+
+#define USBMSC_DRVR_READ(l,b,s,n) ((l)->inode->u.i_bops->read((l)->inode,b,s,n))
+#define USBMSC_DRVR_WRITE(l,b,s,n) ((l)->inode->u.i_bops->write((l)->inode,b,s,n))
+#define USBMSC_DRVR_GEOMETRY(l,g) ((l)->inode->u.i_bops->geometry((l)->inode,g))
+
+/* Everpresent MIN/MAX macros ***********************************************/
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef MAX
+# define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+/* Endpoint descriptors */
+
+enum usbmsc_epdesc_e
+{
+ USBMSC_EPFSBULKOUT = 0, /* Full speed bulk OUT endpoint descriptor */
+ USBMSC_EPFSBULKIN /* Full speed bulk IN endpoint descriptor */
+#ifdef CONFIG_USBDEV_DUALSPEED
+ ,
+ USBMSC_EPHSBULKOUT, /* High speed bulk OUT endpoint descriptor */
+ USBMSC_EPHSBULKIN /* High speed bulk IN endpoint descriptor */
+#endif
+};
+
+/* Container to support a list of requests */
+
+struct usbmsc_req_s
+{
+ FAR struct usbmsc_req_s *flink; /* Implements a singly linked list */
+ FAR struct usbdev_req_s *req; /* The contained request */
+};
+
+/* This structure describes one LUN: */
+
+struct usbmsc_lun_s
+{
+ struct inode *inode; /* Inode structure of open'ed block driver */
+ uint8_t readonly:1; /* Media is read-only */
+ uint8_t locked:1; /* Media removal is prevented */
+ uint16_t sectorsize; /* The size of one sector */
+ uint32_t sd; /* Sense data */
+ uint32_t sdinfo; /* Sense data information */
+ uint32_t uad; /* Unit needs attention data */
+ off_t startsector; /* Sector offset to start of partition */
+ size_t nsectors; /* Number of sectors in the partition */
+};
+
+/* Describes the overall state of the driver */
+
+struct usbmsc_dev_s
+{
+ FAR struct usbdev_s *usbdev; /* usbdev driver pointer (Non-null if registered) */
+
+ /* Worker thread interface */
+
+ pthread_t thread; /* The worker thread */
+ pthread_mutex_t mutex; /* Mutually exclusive access to resources*/
+ pthread_cond_t cond; /* Used to signal worker thread */
+ volatile uint8_t thstate; /* State of the worker thread */
+ volatile uint16_t theventset; /* Set of pending events signaled to worker thread */
+ volatile uint8_t thvalue; /* Value passed with the event (must persist) */
+
+ /* Storage class configuration and state */
+
+ uint8_t nluns:4; /* Number of LUNs */
+ uint8_t config; /* Configuration number */
+
+ /* Endpoints */
+
+ FAR struct usbdev_ep_s *epbulkin; /* Bulk IN endpoint structure */
+ FAR struct usbdev_ep_s *epbulkout; /* Bulk OUT endpoint structure */
+ FAR struct usbdev_req_s *ctrlreq; /* Control request (for ep0 setup responses) */
+
+ /* SCSI command processing */
+
+ struct usbmsc_lun_s *lun; /* Currently selected LUN */
+ struct usbmsc_lun_s *luntab; /* Allocated table of all LUNs */
+ uint8_t cdb[USBMSC_MAXCDBLEN]; /* Command data (cdb[]) from CBW */
+ uint8_t phaseerror:1; /* Need to send phase sensing status */
+ uint8_t shortpacket:1; /* Host transmission stopped unexpectedly */
+ uint8_t cbwdir:2; /* Direction from CBW. See USBMSC_FLAGS_DIR* definitions */
+ uint8_t cdblen; /* Length of cdb[] from CBW */
+ uint8_t cbwlun; /* LUN from the CBW */
+ uint16_t nsectbytes; /* Bytes buffered in iobuffer[] */
+ uint16_t nreqbytes; /* Bytes buffered in head write requests */
+ uint16_t iosize; /* Size of iobuffer[] */
+ uint32_t cbwlen; /* Length of data from CBW */
+ uint32_t cbwtag; /* Tag from the CBW */
+ union
+ {
+ uint32_t xfrlen; /* Read/Write: Sectors remaining to be transferred */
+ uint32_t alloclen; /* Other device-to-host: Host allocation length */
+ } u;
+ uint32_t sector; /* Current sector (relative to lun->startsector) */
+ uint32_t residue; /* Untransferred amount reported in the CSW */
+ uint8_t *iobuffer; /* Buffer for data transfers */
+
+ /* Write request list */
+
+ struct sq_queue_s wrreqlist; /* List of empty write request containers */
+ struct sq_queue_s rdreqlist; /* List of filled read request containers */
+
+ /* Pre-allocated write request containers. The write requests will
+ * be linked in a free list (wrreqlist), and used to send requests to
+ * EPBULKIN; Read requests will be queued in the EBULKOUT.
+ */
+
+ struct usbmsc_req_s wrreqs[CONFIG_USBMSC_NWRREQS];
+ struct usbmsc_req_s rdreqs[CONFIG_USBMSC_NRDREQS];
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+# define EXTERN extern "C"
+extern "C"
+{
+#else
+# define EXTERN extern
+#endif
+
+/* String *******************************************************************/
+
+/* Mass storage class vendor/product/serial number strings */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+EXTERN const char g_mscvendorstr[];
+EXTERN const char g_mscproductstr[];
+EXTERN const char g_mscserialstr[];
+
+/* If we are using a composite device, then vendor/product/serial number strings
+ * are provided by the composite device logic.
+ */
+
+#else
+EXTERN const char g_compvendorstr[];
+EXTERN const char g_compproductstr[];
+EXTERN const char g_compserialstr[];
+
+#define g_mscvendorstr g_compvendorstr
+#define g_mscproductstr g_compproductstr
+#define g_mscserialstr g_compserialstr
+#endif
+/************************************************************************************
+ * Public Function Prototypes
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: usbmsc_mkstrdesc
+ *
+ * Description:
+ * Construct a string descriptor
+ *
+ ************************************************************************************/
+
+struct usb_strdesc_s;
+int usbmsc_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc);
+
+/************************************************************************************
+ * Name: usbmsc_getepdesc
+ *
+ * Description:
+ * Return a pointer to the raw device descriptor
+ *
+ ************************************************************************************/
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+FAR const struct usb_devdesc_s *usbmsc_getdevdesc(void);
+#endif
+
+/************************************************************************************
+ * Name: usbmsc_getepdesc
+ *
+ * Description:
+ * Return a pointer to the raw endpoint descriptor (used for configuring endpoints)
+ *
+ ************************************************************************************/
+
+struct usb_epdesc_s;
+FAR const struct usb_epdesc_s *usbmsc_getepdesc(enum usbmsc_epdesc_e epid);
+
+/************************************************************************************
+ * Name: usbmsc_mkcfgdesc
+ *
+ * Description:
+ * Construct the configuration descriptor
+ *
+ ************************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+int16_t usbmsc_mkcfgdesc(FAR uint8_t *buf, uint8_t speed, uint8_t type);
+#else
+int16_t usbmsc_mkcfgdesc(FAR uint8_t *buf);
+#endif
+
+/************************************************************************************
+ * Name: usbmsc_getqualdesc
+ *
+ * Description:
+ * Return a pointer to the raw qual descriptor
+ *
+ ************************************************************************************/
+
+#if !defined(CONFIG_USBMSC_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED)
+FAR const struct usb_qualdesc_s *usbmsc_getqualdesc(void);
+#endif
+
+/****************************************************************************
+ * Name: usbmsc_workerthread
+ *
+ * Description:
+ * This is the main function of the USB storage worker thread. It loops
+ * until USB-related events occur, then processes those events accordingly
+ *
+ ****************************************************************************/
+
+EXTERN void *usbmsc_workerthread(void *arg);
+
+/****************************************************************************
+ * Name: usbmsc_setconfig
+ *
+ * Description:
+ * Set the device configuration by allocating and configuring endpoints and
+ * by allocating and queue read and write requests.
+ *
+ ****************************************************************************/
+
+EXTERN int usbmsc_setconfig(FAR struct usbmsc_dev_s *priv, uint8_t config);
+
+/****************************************************************************
+ * Name: usbmsc_resetconfig
+ *
+ * Description:
+ * Mark the device as not configured and disable all endpoints.
+ *
+ ****************************************************************************/
+
+EXTERN void usbmsc_resetconfig(FAR struct usbmsc_dev_s *priv);
+
+/****************************************************************************
+ * Name: usbmsc_wrcomplete
+ *
+ * Description:
+ * Handle completion of write request. This function probably executes
+ * in the context of an interrupt handler.
+ *
+ ****************************************************************************/
+
+EXTERN void usbmsc_wrcomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+
+/****************************************************************************
+ * Name: usbmsc_rdcomplete
+ *
+ * Description:
+ * Handle completion of read request on the bulk OUT endpoint. This
+ * is handled like the receipt of serial data on the "UART"
+ *
+ ****************************************************************************/
+
+EXTERN void usbmsc_rdcomplete(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *req);
+
+/****************************************************************************
+ * Name: usbmsc_deferredresponse
+ *
+ * Description:
+ * Some EP0 setup request cannot be responded to immediately becuase they
+ * require some asynchronous action from the SCSI worker thread. This
+ * function is provided for the SCSI thread to make that deferred response.
+ * The specific requests that require this deferred response are:
+ *
+ * 1. USB_REQ_SETCONFIGURATION,
+ * 2. USB_REQ_SETINTERFACE, or
+ * 3. USBMSC_REQ_MSRESET
+ *
+ * In all cases, the success reponse is a zero-length packet; the failure
+ * response is an EP0 stall.
+ *
+ * Input parameters:
+ * priv - Private state structure for this USB storage instance
+ * stall - true is the action failed and a stall is required
+ *
+ ****************************************************************************/
+
+EXTERN void usbmsc_deferredresponse(FAR struct usbmsc_dev_s *priv, bool failed);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* #define __DRIVERS_USBDEV_USBMSC_H */
diff --git a/nuttx/drivers/usbdev/usbmsc_desc.c b/nuttx/drivers/usbdev/usbmsc_desc.c
new file mode 100644
index 000000000..6d7561b3f
--- /dev/null
+++ b/nuttx/drivers/usbdev/usbmsc_desc.c
@@ -0,0 +1,421 @@
+/****************************************************************************
+ * drivers/usbdev/usbmsc_desc.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbdev_trace.h>
+
+#include "usbmsc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+/* Descriptors **************************************************************/
+/* Device descriptor. If the USB mass storage device is configured as part
+ * of a composite device, then the device descriptor will be provided by the
+ * composite device logic.
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+static const struct usb_devdesc_s g_devdesc =
+{
+ USB_SIZEOF_DEVDESC, /* len */
+ USB_DESC_TYPE_DEVICE, /* type */
+ {LSBYTE(0x0200), MSBYTE(0x0200)}, /* usb */
+ USB_CLASS_PER_INTERFACE, /* classid */
+ 0, /* subclass */
+ 0, /* protocol */
+ CONFIG_USBMSC_EP0MAXPACKET, /* maxpacketsize */
+ { /* vendor */
+ LSBYTE(CONFIG_USBMSC_VENDORID),
+ MSBYTE(CONFIG_USBMSC_VENDORID)
+ },
+ { /* product */
+ LSBYTE(CONFIG_USBMSC_PRODUCTID),
+ MSBYTE(CONFIG_USBMSC_PRODUCTID) },
+ { /* device */
+ LSBYTE(CONFIG_USBMSC_VERSIONNO),
+ MSBYTE(CONFIG_USBMSC_VERSIONNO)
+ },
+ USBMSC_MANUFACTURERSTRID, /* imfgr */
+ USBMSC_PRODUCTSTRID, /* iproduct */
+ USBMSC_SERIALSTRID, /* serno */
+ USBMSC_NCONFIGS /* nconfigs */
+};
+#endif
+
+/* Configuration descriptor If the USB mass storage device is configured as part
+ * of a composite device, then the configuration descriptor will be provided by the
+ * composite device logic.
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+static const struct usb_cfgdesc_s g_cfgdesc =
+{
+ USB_SIZEOF_CFGDESC, /* len */
+ USB_DESC_TYPE_CONFIG, /* type */
+ { /* totallen */
+ LSBYTE(SIZEOF_USBMSC_CFGDESC),
+ MSBYTE(SIZEOF_USBMSC_CFGDESC)
+ },
+ USBMSC_NINTERFACES, /* ninterfaces */
+ USBMSC_CONFIGID, /* cfgvalue */
+ USBMSC_CONFIGSTRID, /* icfg */
+ USB_CONFIG_ATTR_ONE|SELFPOWERED|REMOTEWAKEUP, /* attr */
+ (CONFIG_USBDEV_MAXPOWER + 1) / 2 /* mxpower */
+};
+#endif
+
+/* Single interface descriptor */
+
+static const struct usb_ifdesc_s g_ifdesc =
+{
+ USB_SIZEOF_IFDESC, /* len */
+ USB_DESC_TYPE_INTERFACE, /* type */
+ USBMSC_INTERFACEID, /* ifno */
+ USBMSC_ALTINTERFACEID, /* alt */
+ USBMSC_NENDPOINTS, /* neps */
+ USB_CLASS_MASS_STORAGE, /* classid */
+ USBMSC_SUBCLASS_SCSI, /* subclass */
+ USBMSC_PROTO_BULKONLY, /* protocol */
+ USBMSC_INTERFACESTRID /* iif */
+};
+
+/* Endpoint descriptors */
+
+static const struct usb_epdesc_s g_fsepbulkoutdesc =
+{
+ USB_SIZEOF_EPDESC, /* len */
+ USB_DESC_TYPE_ENDPOINT, /* type */
+ USBMSC_EPOUTBULK_ADDR, /* addr */
+ USBMSC_EPOUTBULK_ATTR, /* attr */
+ { /* maxpacket */
+ LSBYTE(USBMSC_FSBULKMAXPACKET),
+ MSBYTE(USBMSC_FSBULKMAXPACKET)
+ },
+ 0 /* interval */
+};
+
+static const struct usb_epdesc_s g_fsepbulkindesc =
+{
+ USB_SIZEOF_EPDESC, /* len */
+ USB_DESC_TYPE_ENDPOINT, /* type */
+ USBMSC_EPINBULK_ADDR, /* addr */
+ USBMSC_EPINBULK_ATTR, /* attr */
+ { /* maxpacket */
+ LSBYTE(USBMSC_FSBULKMAXPACKET),
+ MSBYTE(USBMSC_FSBULKMAXPACKET)
+ },
+ 0 /* interval */
+};
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+#ifndef CONFIG_USBMSC_COMPOSITE
+static const struct usb_qualdesc_s g_qualdesc =
+{
+ USB_SIZEOF_QUALDESC, /* len */
+ USB_DESC_TYPE_DEVICEQUALIFIER, /* type */
+ { /* usb */
+ LSBYTE(0x0200),
+ MSBYTE(0x0200)
+ },
+ USB_CLASS_PER_INTERFACE, /* classid */
+ 0, /* subclass */
+ 0, /* protocol */
+ CONFIG_USBMSC_EP0MAXPACKET, /* mxpacketsize */
+ USBMSC_NCONFIGS, /* nconfigs */
+ 0, /* reserved */
+};
+#endif
+
+static const struct usb_epdesc_s g_hsepbulkoutdesc =
+{
+ USB_SIZEOF_EPDESC, /* len */
+ USB_DESC_TYPE_ENDPOINT, /* type */
+ USBMSC_EPOUTBULK_ADDR, /* addr */
+ USBMSC_EPOUTBULK_ATTR, /* attr */
+ { /* maxpacket */
+ LSBYTE(USBMSC_HSBULKMAXPACKET),
+ MSBYTE(USBMSC_HSBULKMAXPACKET)
+ },
+ 0 /* interval */
+};
+
+static const struct usb_epdesc_s g_hsepbulkindesc =
+{
+ USB_SIZEOF_EPDESC, /* len */
+ USB_DESC_TYPE_ENDPOINT, /* type */
+ USBMSC_EPINBULK_ADDR, /* addr */
+ USBMSC_EPINBULK_ATTR, /* attr */
+ { /* maxpacket */
+ LSBYTE(USBMSC_HSBULKMAXPACKET),
+ MSBYTE(USBMSC_HSBULKMAXPACKET)
+ },
+ 0 /* interval */
+};
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+/* Strings ******************************************************************/
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+const char g_mscvendorstr[] = CONFIG_USBMSC_VENDORSTR;
+const char g_mscproductstr[] = CONFIG_USBMSC_PRODUCTSTR;
+const char g_mscserialstr[] = CONFIG_USBMSC_SERIALSTR;
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbmsc_mkstrdesc
+ *
+ * Description:
+ * Construct a string descriptor
+ *
+ ****************************************************************************/
+
+int usbmsc_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc)
+{
+ const char *str;
+ int len;
+ int ndata;
+ int i;
+
+ switch (id)
+ {
+#ifndef CONFIG_USBMSC_COMPOSITE
+ case 0:
+ {
+ /* Descriptor 0 is the language id */
+
+ strdesc->len = 4;
+ strdesc->type = USB_DESC_TYPE_STRING;
+ strdesc->data[0] = LSBYTE(USBMSC_STR_LANGUAGE);
+ strdesc->data[1] = MSBYTE(USBMSC_STR_LANGUAGE);
+ return 4;
+ }
+
+ case USBMSC_MANUFACTURERSTRID:
+ str = g_mscvendorstr;
+ break;
+
+ case USBMSC_PRODUCTSTRID:
+ str = g_mscproductstr;
+ break;
+
+ case USBMSC_SERIALSTRID:
+ str = g_mscserialstr;
+ break;
+#endif
+
+ /* case USBMSC_CONFIGSTRID: */
+ case USBMSC_INTERFACESTRID:
+ str = CONFIG_USBMSC_CONFIGSTR;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* The string is utf16-le. The poor man's utf-8 to utf16-le
+ * conversion below will only handle 7-bit en-us ascii
+ */
+
+ len = strlen(str);
+ for (i = 0, ndata = 0; i < len; i++, ndata += 2)
+ {
+ strdesc->data[ndata] = str[i];
+ strdesc->data[ndata+1] = 0;
+ }
+
+ strdesc->len = ndata+2;
+ strdesc->type = USB_DESC_TYPE_STRING;
+ return strdesc->len;
+}
+
+/****************************************************************************
+ * Name: usbmsc_getepdesc
+ *
+ * Description:
+ * Return a pointer to the raw device descriptor
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+FAR const struct usb_devdesc_s *usbmsc_getdevdesc(void)
+{
+ return &g_devdesc;
+}
+#endif
+
+/****************************************************************************
+ * Name: usbmsc_getepdesc
+ *
+ * Description:
+ * Return a pointer to the raw endpoint descriptor (used for configuring
+ * endpoints)
+ *
+ ****************************************************************************/
+
+FAR const struct usb_epdesc_s *usbmsc_getepdesc(enum usbmsc_epdesc_e epid)
+{
+ switch (epid)
+ {
+ case USBMSC_EPFSBULKOUT: /* Full speed bulk OUT endpoint descriptor */
+ return &g_fsepbulkoutdesc;
+
+ case USBMSC_EPFSBULKIN: /* Full speed bulk IN endpoint descriptor */
+ return &g_fsepbulkindesc;
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ case USBMSC_EPHSBULKOUT: /* High speed bulk OUT endpoint descriptor */
+ return &g_hsepbulkoutdesc;
+
+ case USBMSC_EPHSBULKIN: /* High speed bulk IN endpoint descriptor */
+ return &g_hsepbulkindesc;
+#endif
+ default:
+ return NULL;
+ }
+};
+
+/****************************************************************************
+ * Name: usbmsc_mkcfgdesc
+ *
+ * Description:
+ * Construct the configuration descriptor
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+int16_t usbmsc_mkcfgdesc(uint8_t *buf, uint8_t speed, uint8_t type)
+#else
+int16_t usbmsc_mkcfgdesc(uint8_t *buf)
+#endif
+{
+#ifdef CONFIG_USBDEV_DUALSPEED
+ FAR const struct usb_epdesc_s *epdesc;
+ bool hispeed = (speed == USB_SPEED_HIGH);
+ uint16_t bulkmxpacket;
+#endif
+
+ /* Configuration descriptor. If the USB mass storage device is
+ * configured as part of a composite device, then the configuration
+ * descriptor will be provided by the composite device logic.
+ */
+
+#ifndef CONFIG_USBMSC_COMPOSITE
+ memcpy(buf, &g_cfgdesc, USB_SIZEOF_CFGDESC);
+ buf += USB_SIZEOF_CFGDESC;
+#endif
+
+ /* Copy the canned interface descriptor */
+
+ memcpy(buf, &g_ifdesc, USB_SIZEOF_IFDESC);
+ buf += USB_SIZEOF_IFDESC;
+
+ /* Make the two endpoint configurations */
+
+#ifdef CONFIG_USBDEV_DUALSPEED
+ /* Check for switches between high and full speed */
+
+ hispeed = (speed == USB_SPEED_HIGH);
+ if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG)
+ {
+ hispeed = !hispeed;
+ }
+
+ bulkmxpacket = USBMSC_BULKMAXPACKET(hispeed);
+ epdesc = USBMSC_EPBULKINDESC(hispeed);
+ memcpy(buf, epdesc, USB_SIZEOF_EPDESC);
+ buf += USB_SIZEOF_EPDESC;
+
+ epdesc = USBMSC_EPBULKOUTDESC(hispeed);
+ memcpy(buf, epdesc, USB_SIZEOF_EPDESC);
+#else
+ memcpy(buf, &g_fsepbulkoutdesc, USB_SIZEOF_EPDESC);
+ buf += USB_SIZEOF_EPDESC;
+ memcpy(buf, &g_fsepbulkindesc, USB_SIZEOF_EPDESC);
+#endif
+
+ return SIZEOF_USBMSC_CFGDESC;
+}
+
+/****************************************************************************
+ * Name: usbmsc_getqualdesc
+ *
+ * Description:
+ * Return a pointer to the raw qual descriptor
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_USBMSC_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED)
+FAR const struct usb_qualdesc_s *usbmsc_getqualdesc(void)
+{
+ return &g_qualdesc;
+}
+#endif
+
diff --git a/nuttx/drivers/usbdev/usbmsc_scsi.c b/nuttx/drivers/usbdev/usbmsc_scsi.c
new file mode 100644
index 000000000..ccc967618
--- /dev/null
+++ b/nuttx/drivers/usbdev/usbmsc_scsi.c
@@ -0,0 +1,2667 @@
+/****************************************************************************
+ * drivers/usbdev/usbmsc_scsi.c
+ *
+ * Copyright (C) 2008-2010, 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Mass storage class device. Bulk-only with SCSI subclass.
+ *
+ * References:
+ * "Universal Serial Bus Mass Storage Class, Specification Overview,"
+ * Revision 1.2, USB Implementer's Forum, June 23, 2003.
+ *
+ * "Universal Serial Bus Mass Storage Class, Bulk-Only Transport,"
+ * Revision 1.0, USB Implementer's Forum, September 31, 1999.
+ *
+ * "SCSI Primary Commands - 3 (SPC-3)," American National Standard
+ * for Information Technology, May 4, 2005
+ *
+ * "SCSI Primary Commands - 4 (SPC-4)," American National Standard
+ * for Information Technology, July 19, 2008
+ *
+ * "SCSI Block Commands -2 (SBC-2)," American National Standard
+ * for Information Technology, November 13, 2004
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+#include <errno.h>
+#include <queue.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/scsi.h>
+#include <nuttx/usb/storage.h>
+#include <nuttx/usb/usbdev.h>
+#include <nuttx/usb/usbdev_trace.h>
+
+#include "usbmsc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* Race condition workaround found by David Hewson. This race condition
+ * "seems to relate to stalling the endpoint when a short response is
+ * generated which causes a residue to exist when the CSW would be returned.
+ * I think there's two issues here. The first being if the transfer which
+ * had just been queued before the stall had not completed then it wouldn’t
+ * then complete once the endpoint was stalled? The second is that the
+ * subsequent transfer for the CSW would be dropped on the floor (by the
+ * epsubmit() function) if the end point was still stalled as the control
+ * transfer to resume it hadn't occurred."
+ */
+
+#define CONFIG_USBMSC_RACEWAR 1
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Debug ********************************************************************/
+
+#if defined(CONFIG_DEBUG_VERBOSE) && defined (CONFIG_DEBUG_USB)
+static void usbmsc_dumpdata(const char *msg, const uint8_t *buf,
+ int buflen);
+#else
+# define usbmsc_dumpdata(msg, buf, len)
+#endif
+
+/* Utility Support Functions ************************************************/
+
+static uint16_t usbmsc_getbe16(uint8_t *buf);
+static uint32_t usbmsc_getbe32(uint8_t *buf);
+static void usbmsc_putbe16(uint8_t * buf, uint16_t val);
+static void usbmsc_putbe24(uint8_t *buf, uint32_t val);
+static void usbmsc_putbe32(uint8_t *buf, uint32_t val);
+#if 0 /* not used */
+static uint16_t usbmsc_getle16(uint8_t *buf);
+#endif
+static uint32_t usbmsc_getle32(uint8_t *buf);
+#if 0 /* not used */
+static void usbmsc_putle16(uint8_t * buf, uint16_t val);
+#endif
+static void usbmsc_putle32(uint8_t *buf, uint32_t val);
+
+/* SCSI Command Processing **************************************************/
+
+static inline int usbmsc_cmdtestunitready(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdrequestsense(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf);
+static inline int usbmsc_cmdread6(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdwrite6(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdinquiry(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf);
+static inline int usbmsc_cmdmodeselect6(FAR struct usbmsc_dev_s *priv);
+static int usbmsc_modepage(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf, uint8_t pcpgcode, int *mdlen);
+static inline int usbmsc_cmdmodesense6(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf);
+static inline int usbmsc_cmdstartstopunit(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdpreventmediumremoval(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdreadformatcapacity(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf);
+static inline int usbmsc_cmdreadcapacity10(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf);
+static inline int usbmsc_cmdread10(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdwrite10(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdverify10(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdsynchronizecache10(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdmodeselect10(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdmodesense10(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf);
+static inline int usbmsc_cmdread12(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_cmdwrite12(FAR struct usbmsc_dev_s *priv);
+static inline int usbmsc_setupcmd(FAR struct usbmsc_dev_s *priv,
+ uint8_t cdblen, uint8_t flags);
+
+/* SCSI Worker Thread *******************************************************/
+
+static int usbmsc_idlestate(FAR struct usbmsc_dev_s *priv);
+static int usbmsc_cmdparsestate(FAR struct usbmsc_dev_s *priv);
+static int usbmsc_cmdreadstate(FAR struct usbmsc_dev_s *priv);
+static int usbmsc_cmdwritestate(FAR struct usbmsc_dev_s *priv);
+static int usbmsc_cmdfinishstate(FAR struct usbmsc_dev_s *priv);
+static int usbmsc_cmdstatusstate(FAR struct usbmsc_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Debug
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbmsc_dumpdata
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_VERBOSE) && defined (CONFIG_DEBUG_USB)
+static void usbmsc_dumpdata(const char *msg, const uint8_t *buf, int buflen)
+{
+ int i;
+
+ dbgprintf("%s:", msg);
+ for (i = 0; i < buflen; i++)
+ {
+ dbgprintf(" %02x", buf[i]);
+ }
+ dbgprintf("\n");
+}
+#endif
+
+/****************************************************************************
+ * Utility Support Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbmsc_getbe16
+ *
+ * Description:
+ * Get a 16-bit big-endian value reference by the byte pointer
+ *
+ ****************************************************************************/
+
+static uint16_t usbmsc_getbe16(uint8_t *buf)
+{
+ return ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
+}
+
+/****************************************************************************
+ * Name: usbmsc_getbe32
+ *
+ * Description:
+ * Get a 32-bit big-endian value reference by the byte pointer
+ *
+ ****************************************************************************/
+
+static uint32_t usbmsc_getbe32(uint8_t *buf)
+{
+ return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
+ ((uint32_t)buf[2] << 8) | ((uint32_t)buf[3]);
+}
+
+/****************************************************************************
+ * Name: usbmsc_putbe16
+ *
+ * Description:
+ * Store a 16-bit value in big-endian order to the location specified by
+ * a byte pointer
+ *
+ ****************************************************************************/
+
+static void usbmsc_putbe16(uint8_t * buf, uint16_t val)
+{
+ buf[0] = val >> 8;
+ buf[1] = val;
+}
+
+/****************************************************************************
+ * Name: usbmsc_putbe24
+ *
+ * Description:
+ * Store a 32-bit value in big-endian order to the location specified by
+ * a byte pointer
+ *
+ ****************************************************************************/
+
+static void usbmsc_putbe24(uint8_t *buf, uint32_t val)
+{
+ buf[0] = val >> 16;
+ buf[1] = val >> 8;
+ buf[2] = val;
+}
+
+/****************************************************************************
+ * Name: usbmsc_putbe32
+ *
+ * Description:
+ * Store a 32-bit value in big-endian order to the location specified by
+ * a byte pointer
+ *
+ ****************************************************************************/
+
+static void usbmsc_putbe32(uint8_t *buf, uint32_t val)
+{
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+}
+
+/****************************************************************************
+ * Name: usbmsc_getle16
+ *
+ * Description:
+ * Get a 16-bit little-endian value reference by the byte pointer
+ *
+ ****************************************************************************/
+
+#if 0 /* not used */
+static uint16_t usbmsc_getle16(uint8_t *buf)
+{
+ return ((uint16_t)buf[1] << 8) | ((uint16_t)buf[0]);
+}
+#endif
+
+/****************************************************************************
+ * Name: usbmsc_getle32
+ *
+ * Description:
+ * Get a 32-bit little-endian value reference by the byte pointer
+ *
+ ****************************************************************************/
+
+static uint32_t usbmsc_getle32(uint8_t *buf)
+{
+ return ((uint32_t)buf[3] << 24) | ((uint32_t)buf[2] << 16) |
+ ((uint32_t)buf[1] << 8) | ((uint32_t)buf[0]);
+}
+
+/****************************************************************************
+ * Name: usbmsc_putle16
+ *
+ * Description:
+ * Store a 16-bit value in little-endian order to the location specified by
+ * a byte pointer
+ *
+ ****************************************************************************/
+
+#if 0 /* not used */
+static void usbmsc_putle16(uint8_t * buf, uint16_t val)
+{
+ buf[0] = val;
+ buf[1] = val >> 8;
+}
+#endif
+
+/****************************************************************************
+ * Name: usbmsc_putle32
+ *
+ * Description:
+ * Store a 32-bit value in little-endian order to the location specified by
+ * a byte pointer
+ *
+ ****************************************************************************/
+
+static void usbmsc_putle32(uint8_t *buf, uint32_t val)
+{
+ buf[0] = val;
+ buf[1] = val >> 8;
+ buf[2] = val >> 16;
+ buf[3] = val >> 24;
+}
+
+/****************************************************************************
+ * SCSI Worker Thread
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbmsc_cmdtestunitready
+ *
+ * Description:
+ * Handle the SCSI_CMD_TESTUNITREADY command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdtestunitready(FAR struct usbmsc_dev_s *priv)
+{
+ int ret;
+
+ priv->u.alloclen = 0;
+ ret = usbmsc_setupcmd(priv, 6, USBMSC_FLAGS_DIRNONE);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdrequestsense
+ *
+ * Description:
+ * Handle the SCSI_CMD_REQUESTSENSE command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdrequestsense(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf)
+{
+ FAR struct scsicmd_requestsense_s *request = (FAR struct scsicmd_requestsense_s *)priv->cdb;
+ FAR struct scsiresp_fixedsensedata_s *response = (FAR struct scsiresp_fixedsensedata_s *)buf;
+ FAR struct usbmsc_lun_s *lun;
+ uint32_t sd;
+ uint32_t sdinfo;
+ uint8_t cdblen;
+ int ret;
+
+ /* Extract the host allocation length */
+
+ priv->u.alloclen = request->alloclen;
+
+ /* Get the expected length of the command (with hack for MS-Windows 12-byte
+ * REQUEST SENSE command.
+ */
+
+ cdblen = SCSICMD_REQUESTSENSE_SIZEOF;
+ if (cdblen != priv->cdblen)
+ {
+ /* Try MS-Windows REQUEST SENSE with cbw->cdblen == 12 */
+
+ cdblen = SCSICMD_REQUESTSENSE_MSSIZEOF;
+ }
+
+ ret = usbmsc_setupcmd(priv, cdblen,
+ USBMSC_FLAGS_DIRDEVICE2HOST|USBMSC_FLAGS_LUNNOTNEEDED|
+ USBMSC_FLAGS_UACOKAY|USBMSC_FLAGS_RETAINSENSEDATA);
+ if (ret == OK)
+ {
+ lun = priv->lun;
+ if (!lun)
+ {
+ sd = SCSI_KCQIR_INVALIDLUN;
+ sdinfo = 0;
+ }
+ else
+ {
+ /* Get the saved sense data from the LUN structure */
+
+ sd = lun->sd;
+ sdinfo = lun->sdinfo;
+
+ /* Discard the sense data */
+
+ lun->sd = SCSI_KCQ_NOSENSE;
+ lun->sdinfo = 0;
+ }
+
+ /* Create the fixed sense data response */
+
+ memset(response, 0, SCSIRESP_FIXEDSENSEDATA_SIZEOF);
+
+ response->code = SCSIRESP_SENSEDATA_RESPVALID|SCSIRESP_SENSEDATA_CURRENTFIXED;
+ response->flags = (uint8_t)(sd >> 16);
+ usbmsc_putbe32(response->info, sdinfo);
+ response->len = SCSIRESP_FIXEDSENSEDATA_SIZEOF - 7;
+ response->code2 = (uint8_t)(sd >> 8);
+ response->qual2 = (uint8_t)sd;
+
+ priv->nreqbytes = SCSIRESP_FIXEDSENSEDATA_SIZEOF;
+ ret = OK;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdread6
+ *
+ * Description:
+ * Handle the SCSI_CMD_READ6 command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdread6(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct scsicmd_read6_s *read6 = (FAR struct scsicmd_read6_s*)priv->cdb;
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ int ret;
+
+ priv->u.xfrlen = (uint16_t)read6->xfrlen;
+ if (priv->u.xfrlen == 0)
+ {
+ priv->u.xfrlen = 256;
+ }
+
+ ret = usbmsc_setupcmd(priv, SCSICMD_READ6_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST|USBMSC_FLAGS_BLOCKXFR);
+ if (ret == OK)
+ {
+ /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */
+
+ priv->sector = (uint32_t)(read6->mslba & SCSICMD_READ6_MSLBAMASK) << 16 | (uint32_t)usbmsc_getbe16(read6->lslba);
+
+ /* Verify that a block driver has been bound to the LUN */
+
+ if (!lun->inode)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_READ6MEDIANOTPRESENT), 0);
+ lun->sd = SCSI_KCQNR_MEDIANOTPRESENT;
+ ret = -EINVAL;
+ }
+
+ /* Verify that sector lies in the range supported by the block driver */
+
+ else if (priv->sector >= lun->nsectors)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_READ6LBARANGE), 0);
+ lun->sd = SCSI_KCQIR_LBAOUTOFRANGE;
+ ret = -EINVAL;
+ }
+
+ /* Looks like we are good to go */
+
+ else
+ {
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDREAD6), priv->cdb[0]);
+ priv->thstate = USBMSC_STATE_CMDREAD;
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdwrite6
+ *
+ * Description:
+ * Handle the SCSI_CMD_WRITE6 command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdwrite6(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct scsicmd_write6_s *write6 = (FAR struct scsicmd_write6_s *)priv->cdb;
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ int ret;
+
+ priv->u.xfrlen = (uint16_t)write6->xfrlen;
+ if (priv->u.xfrlen == 0)
+ {
+ priv->u.xfrlen = 256;
+ }
+
+ ret = usbmsc_setupcmd(priv, SCSICMD_WRITE6_SIZEOF, USBMSC_FLAGS_DIRHOST2DEVICE|USBMSC_FLAGS_BLOCKXFR);
+ if (ret == OK)
+ {
+ /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */
+
+ priv->sector = (uint32_t)(write6->mslba & SCSICMD_WRITE6_MSLBAMASK) << 16 | (uint32_t)usbmsc_getbe16(write6->lslba);
+
+ /* Verify that a block driver has been bound to the LUN */
+
+ if (!lun->inode)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE6MEDIANOTPRESENT), 0);
+ lun->sd = SCSI_KCQNR_MEDIANOTPRESENT;
+ ret = -EINVAL;
+ }
+
+ /* Check for attempts to write to a read-only device */
+
+ else if (lun->readonly)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE6READONLY), 0);
+ lun->sd = SCSI_KCQWP_COMMANDNOTALLOWED;
+ ret = -EINVAL;
+ }
+
+ /* Verify that sector lies in the range supported by the block driver */
+
+ else if (priv->sector >= lun->nsectors)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE6LBARANGE), 0);
+ lun->sd = SCSI_KCQIR_LBAOUTOFRANGE;
+ ret = -EINVAL;
+ }
+
+ /* Looks like we are good to go */
+
+ else
+ {
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDWRITE6), priv->cdb[0]);
+ priv->thstate = USBMSC_STATE_CMDWRITE;
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdinquiry
+ *
+ * Description:
+ * Handle SCSI_CMD_INQUIRY command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdinquiry(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf)
+{
+ FAR struct scscicmd_inquiry_s *inquiry = (FAR struct scscicmd_inquiry_s *)priv->cdb;
+ FAR struct scsiresp_inquiry_s *response = (FAR struct scsiresp_inquiry_s *)buf;
+ int len;
+ int ret;
+
+ priv->u.alloclen = usbmsc_getbe16(inquiry->alloclen);
+ ret = usbmsc_setupcmd(priv, SCSICMD_INQUIRY_SIZEOF,
+ USBMSC_FLAGS_DIRDEVICE2HOST|USBMSC_FLAGS_LUNNOTNEEDED|USBMSC_FLAGS_UACOKAY);
+ if (ret == OK)
+ {
+ if (!priv->lun)
+ {
+ response->qualtype = SCSIRESP_INQUIRYPQ_NOTCAPABLE|SCSIRESP_INQUIRYPD_UNKNOWN;
+ }
+ else if ((inquiry->flags != 0) || (inquiry->pagecode != 0))
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_INQUIRYFLAGS), 0);
+ priv->lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ ret = -EINVAL;
+ }
+ else
+ {
+ memset(response, 0, SCSIRESP_INQUIRY_SIZEOF);
+ priv->nreqbytes = SCSIRESP_INQUIRY_SIZEOF;
+
+#ifdef CONFIG_USBMSC_REMOVABLE
+ response->flags1 = SCSIRESP_INQUIRYFLAGS1_RMB;
+#endif
+ response->version = 2; /* SCSI-2 */
+ response->flags2 = 2; /* SCSI-2 INQUIRY response data format */
+ response->len = SCSIRESP_INQUIRY_SIZEOF - 5;
+
+ /* Strings */
+
+ memset(response->vendorid, ' ', 8+16+4);
+
+ len = strlen(g_mscvendorstr);
+ DEBUGASSERT(len <= 8);
+ memcpy(response->vendorid, g_mscvendorstr, len);
+
+ len = strlen(g_mscproductstr);
+ DEBUGASSERT(len <= 16);
+ memcpy(response->productid, g_mscproductstr, len);
+
+ len = strlen(g_mscserialstr);
+ DEBUGASSERT(len <= 4);
+ memcpy(response->revision, g_mscserialstr, len);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdmodeselect6
+ *
+ * Description:
+ * Handle SCSI_CMD_MODESELECT6 command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdmodeselect6(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct scsicmd_modeselect6_s *modeselect = (FAR struct scsicmd_modeselect6_s *)priv->cdb;
+
+ priv->u.alloclen = modeselect->plen;
+ (void)usbmsc_setupcmd(priv, SCSICMD_MODESELECT6_SIZEOF, USBMSC_FLAGS_DIRHOST2DEVICE);
+
+ /* Not supported */
+
+ priv->lun->sd = SCSI_KCQIR_INVALIDCOMMAND;
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: usbmsc_modepage
+ *
+ * Description:
+ * Common logic for usbmsc_cmdmodesense6() and usbmsc_cmdmodesense10()
+ *
+ ****************************************************************************/
+
+static int usbmsc_modepage(FAR struct usbmsc_dev_s *priv, FAR uint8_t *buf,
+ uint8_t pcpgcode, int *mdlen)
+{
+ FAR struct scsiresp_cachingmodepage_s *cmp = (FAR struct scsiresp_cachingmodepage_s *)buf;
+
+ /* Saving parms not supported */
+
+ if ((pcpgcode & SCSICMD_MODESENSE_PCMASK) == SCSICMD_MODESENSE_PCSAVED)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_PCSAVED), 0);
+ priv->lun->sd = SCSI_KCQIR_SAVINGPARMSNOTSUPPORTED;
+ return -EINVAL;
+ }
+
+ /* Only the caching mode page is supported: */
+
+ if ((pcpgcode & SCSICMD_MODESENSE_PGCODEMASK) == SCSIRESP_MODESENSE_PGCCODE_CACHING ||
+ (pcpgcode & SCSICMD_MODESENSE_PGCODEMASK) == SCSIRESP_MODESENSE_PGCCODE_RETURNALL)
+ {
+ memset(cmp, 0, 12);
+ cmp->pgcode = SCSIRESP_MODESENSE_PGCCODE_CACHING;
+ cmp->len = 10; /* n-2 */
+
+ /* None of the fields are changeable */
+
+ if (((pcpgcode & SCSICMD_MODESENSE_PCMASK) != SCSICMD_MODESENSE_PCCHANGEABLE))
+ {
+ cmp->flags1 = SCSIRESP_CACHINGMODEPG_WCE; /* Write cache enable */
+ cmp->dpflen[0] = 0xff; /* Disable prefetch transfer length = 0xffffffff */
+ cmp->dpflen[1] = 0xff;
+ cmp->maxpf[0] = 0xff; /* Maximum pre-fetch = 0xffffffff */
+ cmp->maxpf[1] = 0xff;
+ cmp->maxpfc[0] = 0xff; /* Maximum pref-fetch ceiling = 0xffffffff */
+ cmp->maxpfc[1] = 0xff;
+ }
+
+ /* Return the mode data length */
+
+ *mdlen = 12; /* Only the first 12-bytes of caching mode page sent */
+ return OK;
+ }
+ else
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_MODEPAGEFLAGS), pcpgcode);
+ priv->lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ return -EINVAL;
+ }
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdmodesense6
+ *
+ * Description:
+ * Handle SCSI_CMD_MODESENSE6 command
+ *
+ ****************************************************************************/
+
+static int inline usbmsc_cmdmodesense6(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf)
+{
+ FAR struct scsicmd_modesense6_s *modesense = (FAR struct scsicmd_modesense6_s *)priv->cdb;
+ FAR struct scsiresp_modeparameterhdr6_s *mph = (FAR struct scsiresp_modeparameterhdr6_s *)buf;
+ int mdlen;
+ int ret;
+
+ priv->u.alloclen = modesense->alloclen;
+ ret = usbmsc_setupcmd(priv, SCSICMD_MODESENSE6_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST);
+ if (ret == OK)
+ {
+ if ((modesense->flags & ~SCSICMD_MODESENSE6_DBD) != 0 || modesense->subpgcode != 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_MODESENSE6FLAGS), 0);
+ priv->lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ ret = -EINVAL;
+ }
+ else
+ {
+ /* The response consists of:
+ *
+ * (1) A MODESENSE6-specific mode parameter header,
+ * (2) A variable length list of block descriptors, and
+ * (3) A variable length list of mode page formats
+ */
+
+ mph->type = 0; /* Medium type */
+ mph->param = (priv->lun->readonly ? SCSIRESP_MODEPARMHDR_DAPARM_WP : 0x00);
+ mph->bdlen = 0; /* Block descriptor length */
+
+ /* There are no block descriptors, only the following mode page: */
+
+ ret = usbmsc_modepage(priv, &buf[SCSIRESP_MODEPARAMETERHDR6_SIZEOF], modesense->pcpgcode, &mdlen);
+ if (ret == OK)
+ {
+ /* Store the mode data length and return the total message size */
+
+ mph->mdlen = mdlen - 1;
+ priv->nreqbytes = mdlen + SCSIRESP_MODEPARAMETERHDR6_SIZEOF;
+ }
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdstartstopunit
+ *
+ * Description:
+ * Handle SCSI_CMD_STARTSTOPUNIT command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdstartstopunit(FAR struct usbmsc_dev_s *priv)
+{
+ int ret;
+
+ priv->u.alloclen = 0;
+ ret = usbmsc_setupcmd(priv, SCSICMD_STARTSTOPUNIT_SIZEOF, USBMSC_FLAGS_DIRNONE);
+ if (ret == OK)
+ {
+#ifndef CONFIG_USBMSC_REMOVABLE
+ /* This command is not valid if the media is not removable */
+
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_NOTREMOVABLE), 0);
+ lun->sd = SCSI_KCQIR_INVALIDCOMMAND;
+ ret = -EINVAL;
+#endif
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdpreventmediumremoval
+ *
+ * Description:
+ * Handle SCSI_CMD_PREVENTMEDIAREMOVAL command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdpreventmediumremoval(FAR struct usbmsc_dev_s *priv)
+{
+#ifdef CONFIG_USBMSC_REMOVABLE
+ FAR struct scsicmd_preventmediumremoval_s *pmr = (FAR struct scsicmd_preventmediumremoval_s *)priv->cdb;
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+#endif
+ int ret;
+
+ priv->u.alloclen = 0;
+ ret = usbmsc_setupcmd(priv, SCSICMD_PREVENTMEDIUMREMOVAL_SIZEOF, USBMSC_FLAGS_DIRNONE);
+ if (ret == OK)
+ {
+#ifndef CONFIG_USBMSC_REMOVABLE
+ lun->sd = SCSI_KCQIR_INVALIDCOMMAND;
+ ret = -EINVAL;
+#else
+ if ((pmr->prevent & ~SCSICMD_PREVENTMEDIUMREMOVAL_TRANSPORT) != 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_PREVENTMEDIUMREMOVALPREVENT), 0);
+ lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ ret = -EINVAL;
+ }
+
+ lun->locked = pmr->prevent & SCSICMD_PREVENTMEDIUMREMOVAL_TRANSPORT;
+#endif
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdreadformatcapacity
+ *
+ * Description:
+ * Handle SCSI_CMD_READFORMATCAPACITIES command (MMC)
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdreadformatcapacity(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf)
+{
+ FAR struct scsicmd_readformatcapcacities_s *rfc = (FAR struct scsicmd_readformatcapcacities_s *)priv->cdb;
+ FAR struct scsiresp_readformatcapacities_s *hdr;
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ int ret;
+
+ priv->u.alloclen = usbmsc_getbe16(rfc->alloclen);
+ ret = usbmsc_setupcmd(priv, SCSICMD_READFORMATCAPACITIES_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST);
+ if (ret == OK)
+ {
+ hdr = (FAR struct scsiresp_readformatcapacities_s *)buf;
+ memset(hdr, 0, SCSIRESP_READFORMATCAPACITIES_SIZEOF);
+ hdr->listlen = SCSIRESP_CURRCAPACITYDESC_SIZEOF;
+
+ /* Only the Current/Maximum Capacity Descriptor follows the header */
+
+ usbmsc_putbe32(hdr->nblocks, lun->nsectors);
+ hdr->type = SCIRESP_RDFMTCAPACITIES_FORMATED;
+ usbmsc_putbe24(hdr->blocklen, lun->sectorsize);
+ priv->nreqbytes = SCSIRESP_READFORMATCAPACITIES_SIZEOF;
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdreadcapacity10
+ *
+ * Description:
+ * Handle SCSI_CMD_READCAPACITY10 command
+ *
+ ****************************************************************************/
+
+static int inline usbmsc_cmdreadcapacity10(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf)
+{
+ FAR struct scsicmd_readcapacity10_s *rcc = (FAR struct scsicmd_readcapacity10_s *)priv->cdb;
+ FAR struct scsiresp_readcapacity10_s *rcr = (FAR struct scsiresp_readcapacity10_s *)buf;
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ uint32_t lba;
+ int ret;
+
+ priv->u.alloclen = SCSIRESP_READCAPACITY10_SIZEOF; /* Fake the allocation length */
+ ret = usbmsc_setupcmd(priv, SCSICMD_READCAPACITY10_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST);
+ if (ret == OK)
+ {
+ /* Check the PMI and LBA fields */
+
+ lba = usbmsc_getbe32(rcc->lba);
+
+ if (rcc->pmi > 1 || (rcc->pmi == 0 && lba != 0))
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_READCAPACITYFLAGS), 0);
+ lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ ret = -EINVAL;
+ }
+ else
+ {
+ usbmsc_putbe32(rcr->lba, lun->nsectors - 1);
+ usbmsc_putbe32(&buf[4], lun->sectorsize);
+ priv->nreqbytes = SCSIRESP_READCAPACITY10_SIZEOF;
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdread10
+ *
+ * Description:
+ * Handle SCSI_CMD_READ10 command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdread10(FAR struct usbmsc_dev_s *priv)
+{
+ struct scsicmd_read10_s *read10 = (struct scsicmd_read10_s*)priv->cdb;
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ int ret;
+
+ priv->u.xfrlen = usbmsc_getbe16(read10->xfrlen);
+ ret = usbmsc_setupcmd(priv, SCSICMD_READ10_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST|USBMSC_FLAGS_BLOCKXFR);
+ if (ret == OK)
+ {
+ /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */
+
+ priv->sector = usbmsc_getbe32(read10->lba);
+
+ /* Verify that we can support this read command */
+
+ if ((read10->flags & ~(SCSICMD_READ10FLAGS_DPO|SCSICMD_READ10FLAGS_FUA)) != 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_READ10FLAGS), 0);
+ lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ ret = -EINVAL;
+ }
+
+ /* Verify that a block driver has been bound to the LUN */
+
+ else if (!lun->inode)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_READ10MEDIANOTPRESENT), 0);
+ lun->sd = SCSI_KCQNR_MEDIANOTPRESENT;
+ ret = -EINVAL;
+ }
+
+ /* Verify that LBA lies in the range supported by the block driver */
+
+ else if (priv->sector >= lun->nsectors)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_READ10LBARANGE), 0);
+ lun->sd = SCSI_KCQIR_LBAOUTOFRANGE;
+ ret = -EINVAL;
+ }
+
+ /* Looks like we are good to go */
+
+ else
+ {
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDREAD10), priv->cdb[0]);
+ priv->thstate = USBMSC_STATE_CMDREAD;
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdwrite10
+ *
+ * Description:
+ * Handle SCSI_CMD_WRITE10 command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdwrite10(FAR struct usbmsc_dev_s *priv)
+{
+ struct scsicmd_write10_s *write10 = (struct scsicmd_write10_s *)priv->cdb;
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ int ret;
+
+ priv->u.xfrlen = usbmsc_getbe16(write10->xfrlen);
+ ret = usbmsc_setupcmd(priv, SCSICMD_WRITE10_SIZEOF, USBMSC_FLAGS_DIRHOST2DEVICE|USBMSC_FLAGS_BLOCKXFR);
+ if (ret == OK)
+ {
+ /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */
+
+ priv->sector = usbmsc_getbe32(write10->lba);
+
+ /* Verify that we can support this write command */
+
+ if ((write10->flags & ~(SCSICMD_WRITE10FLAGS_DPO|SCSICMD_WRITE10FLAGS_FUA)) != 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE10FLAGS), 0);
+ lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ ret = -EINVAL;
+ }
+
+ /* Verify that a block driver has been bound to the LUN */
+
+ else if (!lun->inode)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE10MEDIANOTPRESENT), 0);
+ lun->sd = SCSI_KCQNR_MEDIANOTPRESENT;
+ ret = -EINVAL;
+ }
+
+ /* Check for attempts to write to a read-only device */
+
+ else if (lun->readonly)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE10READONLY), 0);
+ lun->sd = SCSI_KCQWP_COMMANDNOTALLOWED;
+ ret = -EINVAL;
+ }
+
+ /* Verify that LBA lies in the range supported by the block driver */
+
+ else if (priv->sector >= lun->nsectors)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE10LBARANGE), 0);
+ lun->sd = SCSI_KCQIR_LBAOUTOFRANGE;
+ ret = -EINVAL;
+ }
+
+ /* Looks like we are good to go */
+
+ else
+ {
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDWRITE10), priv->cdb[0]);
+ priv->thstate = USBMSC_STATE_CMDWRITE;
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdverify10
+ *
+ * Description:
+ * Handle SCSI_CMD_VERIFY10 command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdverify10(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct scsicmd_verify10_s *verf = (FAR struct scsicmd_verify10_s *)priv->cdb;
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ uint32_t lba;
+ uint16_t blocks;
+ size_t sector;
+ ssize_t nread;
+ int ret;
+ int i;
+
+ priv->u.alloclen = 0;
+ ret = usbmsc_setupcmd(priv, SCSICMD_VERIFY10_SIZEOF, USBMSC_FLAGS_DIRNONE);
+ if (ret == OK)
+ {
+ /* Verify the starting and ending LBA */
+
+ lba = usbmsc_getbe32(verf->lba);
+ blocks = usbmsc_getbe16(verf->len);
+
+ if ((verf->flags & ~SCSICMD_VERIFY10_DPO) != 0 || verf->groupno != 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_VERIFY10FLAGS), 0);
+ lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ ret = -EINVAL;
+ }
+ else if (blocks == 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_VERIFY10NOBLOCKS), 0);
+ ret = -EIO; /* No reply */
+ }
+
+ /* Verify that a block driver has been bound to the LUN */
+
+ else if (!lun->inode)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_VERIFY10MEDIANOTPRESENT), 0);
+ lun->sd = SCSI_KCQNR_MEDIANOTPRESENT;
+ ret = -EINVAL;
+ }
+
+ /* Verify that LBA lies in the range supported by the block driver */
+
+ else if (lba >= lun->nsectors || lba + blocks > lun->nsectors)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_VERIFY10LBARANGE), 0);
+ lun->sd = SCSI_KCQIR_LBAOUTOFRANGE;
+ ret = -EINVAL;
+ }
+ else
+ {
+ /* Try to read the requested blocks */
+
+ for (i = 0, sector = lba + lun->startsector; i < blocks; i++, sector++)
+ {
+ nread = USBMSC_DRVR_READ(lun, priv->iobuffer, sector, 1);
+ if (nread < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_VERIFY10READFAIL), i);
+ lun->sd = SCSI_KCQME_UNRRE1;
+ lun->sdinfo = sector;
+ ret = -EIO;
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdsynchronizecache10
+ *
+ * Description:
+ * Handle SCSI_CMD_SYNCHCACHE10 command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdsynchronizecache10(FAR struct usbmsc_dev_s *priv)
+{
+ int ret;
+
+ priv->u.alloclen = 0;
+
+ /* Verify that we have the LUN structure and the block driver has been bound */
+
+ if (!priv->lun->inode)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_SYNCCACHEMEDIANOTPRESENT), 0);
+ priv->lun->sd = SCSI_KCQNR_MEDIANOTPRESENT;
+ ret = -EINVAL;
+ }
+ else
+ {
+ ret = usbmsc_setupcmd(priv, SCSICMD_SYNCHRONIZECACHE10_SIZEOF, USBMSC_FLAGS_DIRNONE);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdmodeselect10
+ *
+ * Description:
+ * Handle SCSI_CMD_MODESELECT10 command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdmodeselect10(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct scsicmd_modeselect10_s *modeselect = (FAR struct scsicmd_modeselect10_s *)priv->cdb;
+
+ priv->u.alloclen = usbmsc_getbe16(modeselect->parmlen);
+ (void)usbmsc_setupcmd(priv, SCSICMD_MODESELECT10_SIZEOF, USBMSC_FLAGS_DIRHOST2DEVICE);
+
+ /* Not supported */
+
+ priv->lun->sd = SCSI_KCQIR_INVALIDCOMMAND;
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdmodesense10
+ *
+ * Description:
+ * Handle SCSI_CMD_MODESENSE10 command
+ *
+ ****************************************************************************/
+
+static int inline usbmsc_cmdmodesense10(FAR struct usbmsc_dev_s *priv,
+ FAR uint8_t *buf)
+{
+ FAR struct scsicmd_modesense10_s *modesense = (FAR struct scsicmd_modesense10_s *)priv->cdb;
+ FAR struct scsiresp_modeparameterhdr10_s *mph = (FAR struct scsiresp_modeparameterhdr10_s *)buf;
+ int mdlen;
+ int ret;
+
+ priv->u.alloclen = usbmsc_getbe16(modesense->alloclen);
+ ret = usbmsc_setupcmd(priv, SCSICMD_MODESENSE10_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST);
+ if (ret == OK)
+ {
+ if ((modesense->flags & ~SCSICMD_MODESENSE10_DBD) != 0 || modesense->subpgcode != 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_MODESENSE10FLAGS), 0);
+ priv->lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ ret = -EINVAL;
+ }
+ else
+ {
+ /* The response consists of:
+ *
+ * (1) A MODESENSE6-specific mode parameter header,
+ * (2) A variable length list of block descriptors, and
+ * (3) A variable lengtth list of mode page formats
+ */
+
+ memset(mph, 0, SCSIRESP_MODEPARAMETERHDR10_SIZEOF);
+ mph->param = (priv->lun->readonly ? SCSIRESP_MODEPARMHDR_DAPARM_WP : 0x00);
+
+ /* There are no block descriptors, only the following mode page: */
+
+ ret = usbmsc_modepage(priv, &buf[SCSIRESP_MODEPARAMETERHDR10_SIZEOF], modesense->pcpgcode, &mdlen);
+ if (ret == OK)
+ {
+ /* Store the mode data length and return the total message size */
+
+ usbmsc_putbe16(mph->mdlen, mdlen - 2);
+ priv->nreqbytes = mdlen + SCSIRESP_MODEPARAMETERHDR10_SIZEOF;
+ }
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdread12
+ *
+ * Description:
+ * Handle SCSI_CMD_READ12 command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdread12(FAR struct usbmsc_dev_s *priv)
+{
+ struct scsicmd_read12_s *read12 = (struct scsicmd_read12_s*)priv->cdb;
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ int ret;
+
+ priv->u.xfrlen = usbmsc_getbe32(read12->xfrlen);
+ ret = usbmsc_setupcmd(priv, SCSICMD_READ12_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST|USBMSC_FLAGS_BLOCKXFR);
+ if (ret == OK)
+ {
+ /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */
+
+ priv->sector = usbmsc_getbe32(read12->lba);
+
+ /* Verify that we can support this read command */
+
+ if ((read12->flags & ~(SCSICMD_READ12FLAGS_DPO|SCSICMD_READ12FLAGS_FUA)) != 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_READ12FLAGS), 0);
+ lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ ret = -EINVAL;
+ }
+
+ /* Verify that a block driver has been bound to the LUN */
+
+ else if (!lun->inode)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_READ12MEDIANOTPRESENT), 0);
+ lun->sd = SCSI_KCQNR_MEDIANOTPRESENT;
+ ret = -EINVAL;
+ }
+
+ /* Verify that LBA lies in the range supported by the block driver */
+
+ else if (priv->sector >= lun->nsectors)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_READ12LBARANGE), 0);
+ lun->sd = SCSI_KCQIR_LBAOUTOFRANGE;
+ ret = -EINVAL;
+ }
+
+ /* Looks like we are good to go */
+
+ else
+ {
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDREAD12), priv->cdb[0]);
+ priv->thstate = USBMSC_STATE_CMDREAD;
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdwrite12
+ *
+ * Description:
+ * Handle SCSI_CMD_WRITE12 command
+ *
+ ****************************************************************************/
+
+static inline int usbmsc_cmdwrite12(FAR struct usbmsc_dev_s *priv)
+{
+ struct scsicmd_write12_s *write12 = (struct scsicmd_write12_s *)priv->cdb;
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ int ret;
+
+ priv->u.xfrlen = usbmsc_getbe32(write12->xfrlen);
+ ret = usbmsc_setupcmd(priv, SCSICMD_WRITE12_SIZEOF, USBMSC_FLAGS_DIRHOST2DEVICE|USBMSC_FLAGS_BLOCKXFR);
+ if (ret == OK)
+ {
+ /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */
+
+ priv->sector = usbmsc_getbe32(write12->lba);
+
+ /* Verify that we can support this write command */
+
+ if ((write12->flags & ~(SCSICMD_WRITE12FLAGS_DPO|SCSICMD_WRITE12FLAGS_FUA)) != 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE12FLAGS), 0);
+ lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ }
+
+ /* Verify that a block driver has been bound to the LUN */
+
+ else if (!lun->inode)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE12MEDIANOTPRESENT), 0);
+ lun->sd = SCSI_KCQNR_MEDIANOTPRESENT;
+ ret = -EINVAL;
+ }
+
+ /* Check for attempts to write to a read-only device */
+
+ else if (lun->readonly)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE12READONLY), 0);
+ lun->sd = SCSI_KCQWP_COMMANDNOTALLOWED;
+ }
+
+ /* Verify that LBA lies in the range supported by the block driver */
+
+ else if (priv->sector >= lun->nsectors)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_WRITE12LBARANGE), 0);
+ lun->sd = SCSI_KCQIR_LBAOUTOFRANGE;
+ }
+
+ /* Looks like we are good to go */
+
+ else
+ {
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDWRITE12), priv->cdb[0]);
+ priv->thstate = USBMSC_STATE_CMDWRITE;
+ return OK;
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_setupcmd
+ *
+ * Description:
+ * Called after each SCSI command is identified in order to perform setup
+ * and verification operations that are common to all SCSI commands. This
+ * function performs the following common setup operations:
+ *
+ * 1. Determine the direction of the response
+ * 2. Verify lengths
+ * 3. Setup and verify the LUN
+ *
+ * Includes special logic for INQUIRY and REQUESTSENSE commands
+ *
+ ****************************************************************************/
+
+static int inline usbmsc_setupcmd(FAR struct usbmsc_dev_s *priv, uint8_t cdblen, uint8_t flags)
+{
+ FAR struct usbmsc_lun_s *lun = NULL;
+ uint32_t datlen;
+ uint8_t dir = flags & USBMSC_FLAGS_DIRMASK;
+ int ret = OK;
+
+ /* Verify the LUN and set up the current LUN reference in the
+ * device structure
+ */
+
+ if (priv->cbwlun < priv->nluns)
+ {
+ /* LUN number is valid in a valid range, but the LUN is not necessarily
+ * bound to a block driver. That will be checked as necessary in each
+ * individual command.
+ */
+
+ lun = &priv->luntab[priv->cbwlun];
+ priv->lun = lun;
+ }
+
+ /* Only a few commands may specify unsupported LUNs */
+
+ else if ((flags & USBMSC_FLAGS_LUNNOTNEEDED) == 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDBADLUN), priv->cbwlun);
+ ret = -EINVAL;
+ }
+
+ /* Extract the direction and data transfer length */
+
+ dir = flags & USBMSC_FLAGS_DIRMASK; /* Expected data direction */
+ datlen = 0;
+ if ((flags & USBMSC_FLAGS_BLOCKXFR) == 0)
+ {
+ /* Non-block transfer. Data length: Host allocation to receive data
+ * (only for device-to-host transfers. At present, alloclen is set
+ * to zero for all host-to-device, non-block transfers.
+ */
+
+ datlen = priv->u.alloclen;
+ }
+ else if (lun)
+ {
+ /* Block transfer: Calculate the total size of all sectors to be transferred */
+
+ datlen = priv->u.alloclen * lun->sectorsize;
+ }
+
+ /* Check the data direction. This was set up at the end of the
+ * IDLE state when the CBW was parsed, but if there is no data,
+ * then the direction is none.
+ */
+
+ if (datlen == 0)
+ {
+ /* No data.. then direction is none */
+
+ dir = USBMSC_FLAGS_DIRNONE;
+ }
+
+ /* Compare the expected data length in the command to the data length
+ * in the CBW.
+ */
+
+ else if (priv->cbwlen < datlen)
+ {
+ /* Clip to the length in the CBW and declare a phase error */
+
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_PHASEERROR1), priv->cdb[0]);
+ if ((flags & USBMSC_FLAGS_BLOCKXFR) != 0)
+ {
+ priv->u.alloclen = priv->cbwlen;
+ }
+ else
+ {
+ priv->u.xfrlen = priv->cbwlen / lun->sectorsize;
+ }
+
+ priv->phaseerror = 1;
+ }
+
+ /* Initialize the residue */
+
+ priv->residue = priv->cbwlen;
+
+ /* Conflicting data directions is a phase error */
+
+ if (priv->cbwdir != dir && datlen > 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_PHASEERROR2), priv->cdb[0]);
+ priv->phaseerror = 1;
+ ret = -EINVAL;
+ }
+
+ /* Compare the length of data in the cdb[] with the expected length
+ * of the command.
+ */
+
+ if (cdblen != priv->cdblen)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_PHASEERROR3), priv->cdb[0]);
+ priv->phaseerror = 1;
+ ret = -EINVAL;
+ }
+
+ if (lun)
+ {
+ /* Retain the sense data for the REQUEST SENSE command */
+
+ if ((flags & USBMSC_FLAGS_RETAINSENSEDATA) == 0)
+ {
+ /* Discard the sense data */
+
+ lun->sd = SCSI_KCQ_NOSENSE;
+ lun->sdinfo = 0;
+ }
+
+ /* If a unit attention condition exists, then only a restricted set of
+ * commands is permitted.
+ */
+
+ if (lun->uad != SCSI_KCQ_NOSENSE && (flags & USBMSC_FLAGS_UACOKAY) != 0)
+ {
+ /* Command not permitted */
+
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDUNEVIOLATION), priv->cbwlun);
+ lun->sd = lun->uad;
+ lun->uad = SCSI_KCQ_NOSENSE;
+ ret = -EINVAL;
+ }
+ }
+
+ /* The final, 1-byte field of every SCSI command is the Control field which
+ * must be zero
+ */
+
+ if (priv->cdb[cdblen-1] != 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_SCSICMDCONTROL), 0);
+ if (lun)
+ {
+ lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA;
+ }
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_idlestate
+ *
+ * Description:
+ * Called from the worker thread in the USBMSC_STATE_IDLE state. Checks
+ * for the receipt of a bulk CBW.
+ *
+ * Returned value:
+ * If no new, valid CBW is available, this function returns a negated errno.
+ * Otherwise, when a new CBW is successfully parsed, this function sets
+ * priv->thstate to USBMSC_STATE_CMDPARSE and returns OK.
+ *
+ ****************************************************************************/
+
+static int usbmsc_idlestate(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct usbmsc_req_s *privreq;
+ FAR struct usbdev_req_s *req;
+ FAR struct usbmsc_cbw_s *cbw;
+ irqstate_t flags;
+ int ret = -EINVAL;
+
+ /* Take a request from the rdreqlist */
+
+ flags = irqsave();
+ privreq = (FAR struct usbmsc_req_s *)sq_remfirst(&priv->rdreqlist);
+ irqrestore(flags);
+
+ /* Has anything been received? If not, just return an error.
+ * This will cause us to remain in the IDLE state. When a USB request is
+ * received, the worker thread will be awakened in the USBMSC_STATE_IDLE
+ * and we will be called again.
+ */
+
+ if (!privreq)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_IDLERDREQLISTEMPTY), 0);
+ return -ENOMEM;
+ }
+
+ req = privreq->req;
+ cbw = (FAR struct usbmsc_cbw_s *)req->buf;
+
+ /* Handle the CBW */
+
+ usbmsc_dumpdata("SCSCI CBW", (uint8_t*)cbw, USBMSC_CBW_SIZEOF - USBMSC_MAXCDBLEN);
+ usbmsc_dumpdata(" CDB", cbw->cdb, MIN(cbw->cdblen, USBMSC_MAXCDBLEN));
+
+ /* Check for properly formatted CBW? */
+
+ if (req->xfrd != USBMSC_CBW_SIZEOF ||
+ cbw->signature[0] != 'U' ||
+ cbw->signature[1] != 'S' ||
+ cbw->signature[2] != 'B' ||
+ cbw->signature[3] != 'C')
+ {
+ /* CBS BAD: Stall the bulk endpoints. If the CBW is bad we must stall the
+ * bulk IN endpoint and either (1) stall the bulk OUT endpoint, or
+ * (2) discard data from the endpoint.
+ */
+
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_INVALIDCBWSIGNATURE), 0);
+ EP_STALL(priv->epbulkout);
+ EP_STALL(priv->epbulkin);
+ }
+
+ /* Is the CBW meaningful? */
+
+ else if (cbw->lun >= priv->nluns || (cbw->flags & ~USBMSC_CBWFLAG_IN) != 0 ||
+ cbw->cdblen < 6 || cbw->cdblen > USBMSC_MAXCDBLEN)
+ {
+ /* CBS BAD: Stall the bulk endpoints. If the CBW is bad we must stall the
+ * bulk IN endpoint and either (1) stall the bulk OUT endpoint, or
+ * (2) discard data from the endpoint.
+ */
+
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_INVALIDCBWCONTENT), 0);
+ EP_STALL(priv->epbulkout);
+ EP_STALL(priv->epbulkin);
+ }
+
+ /* Save the information from the CBW */
+
+ else
+ {
+ /* Extract the CDB and copy the whole CBD[] for later use */
+
+ priv->cdblen = cbw->cdblen;
+ memcpy(priv->cdb, cbw->cdb, priv->cdblen);
+
+ /* Extract the data direction */
+
+ if ((cbw->flags & USBMSC_CBWFLAG_IN) != 0)
+ {
+ /* IN: Device-to-host */
+
+ priv->cbwdir = USBMSC_FLAGS_DIRDEVICE2HOST;
+ }
+ else
+ {
+ /* OUT: Host-to-device */
+
+ priv->cbwdir = USBMSC_FLAGS_DIRHOST2DEVICE;
+ }
+
+ /* Get the max size of the data response */
+
+ priv->cbwlen = usbmsc_getle32(cbw->datlen);
+ if (priv->cbwlen == 0)
+ {
+ /* No length? Then no direction either */
+
+ priv->cbwdir = USBMSC_FLAGS_DIRNONE;
+ }
+
+ /* Extract and save the LUN index and TAG value */
+
+ priv->cbwlun = cbw->lun;
+ priv->cbwtag = usbmsc_getle32(cbw->tag);
+
+ /* Return the read request to the bulk out endpoint for re-filling */
+
+ req = privreq->req;
+ req->len = CONFIG_USBMSC_BULKOUTREQLEN;
+ req->priv = privreq;
+ req->callback = usbmsc_rdcomplete;
+
+ /* Change to the CMDPARSE state and return success */
+
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_IDLECMDPARSE), priv->cdb[0]);
+ priv->thstate = USBMSC_STATE_CMDPARSE;
+ ret = OK;
+ }
+
+ /* In any event, return the request to be refilled */
+
+ if (EP_SUBMIT(priv->epbulkout, req) != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_IDLERDSUBMIT), (uint16_t)-ret);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdparsestate
+ *
+ * Description:
+ * Called from the worker thread in the USBMSC_STATE_CMDPARSE state.
+ * This state is entered when usbmsc_idlestate obtains a valid CBW
+ * containing SCSI commands. This function processes those SCSI commands.
+ *
+ * Returned value:
+ * If no write request is available or certain other errors occur, this
+ * function returns a negated errno and stays in the USBMSC_STATE_CMDPARSE
+ * state. Otherwise, when the new CBW is successfully process, this
+ * function sets priv->thstate to one of USBMSC_STATE_CMDREAD,
+ * USBMSC_STATE_CMDWRITE, or USBMSC_STATE_CMDFINISH and returns OK.
+ *
+ ****************************************************************************/
+
+static int usbmsc_cmdparsestate(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct usbmsc_req_s *privreq;
+ FAR uint8_t *buf;
+ int ret = -EINVAL;
+
+ usbmsc_dumpdata("SCSCI CDB", priv->cdb, priv->cdblen);
+
+ /* Check if there is a request in the wrreqlist that we will be able to
+ * use for data or status.
+ */
+
+ privreq = (FAR struct usbmsc_req_s *)sq_peek(&priv->wrreqlist);
+
+ /* If there no request structures available, then just return an error.
+ * This will cause us to remain in the CMDPARSE state. When a request is
+ * returned, the worker thread will be awakened in the USBMSC_STATE_CMDPARSE
+ * and we will be called again.
+ */
+
+ if (!privreq)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDPARSEWRREQLISTEMPTY), 0);
+ return -ENOMEM;
+ }
+
+ DEBUGASSERT(privreq->req && privreq->req->buf);
+ buf = privreq->req->buf;
+
+ /* Assume that no errors will be encountered */
+
+ priv->phaseerror = 0;
+ priv->shortpacket = 0;
+
+ /* No data is buffered */
+
+ priv->nsectbytes = 0;
+ priv->nreqbytes = 0;
+
+ /* Get exclusive access to the block driver */
+
+ pthread_mutex_lock(&priv->mutex);
+ switch (priv->cdb[0])
+ {
+ case SCSI_CMD_TESTUNITREADY: /* 0x00 Mandatory */
+ ret = usbmsc_cmdtestunitready(priv);
+ break;
+
+ /* case SCSI_CMD_REZEROUNIT: * 0x01 Obsolete
+ * * 0x02 Vendor-specific */
+
+ case SCSI_CMD_REQUESTSENSE: /* 0x03 Mandatory */
+ ret = usbmsc_cmdrequestsense(priv, buf);
+ break;
+
+ /* case SCSI_CMD_FORMAT_UNIT: * 0x04 Mandatory, but not implemented
+ * * 0x05 Vendor specific
+ * * 0x06 Vendor specific
+ * case SCSI_CMD_REASSIGNBLOCKS: * 0x07 Optional */
+
+ case SCSI_CMD_READ6: /* 0x08 Mandatory */
+ ret = usbmsc_cmdread6(priv);
+ break;
+
+ /* * 0x09 Vendor specific */
+
+ case SCSI_CMD_WRITE6: /* 0x0a Optional */
+ ret = usbmsc_cmdwrite6(priv);
+ break;
+
+ /* case SCSI_CMD_SEEK6: * 0x0b Obsolete
+ * * 0x0c-0x10 Vendor specific
+ * case SCSI_CMD_SPACE6: * 0x11 Vendor specific */
+
+ case SCSI_CMD_INQUIRY: /* 0x12 Mandatory */
+ ret = usbmsc_cmdinquiry(priv, buf);
+ break;
+
+ /* * 0x13-0x14 Vendor specific */
+
+ case SCSI_CMD_MODESELECT6: /* 0x15 Optional */
+ ret = usbmsc_cmdmodeselect6(priv);
+ break;
+
+ /* case SCSI_CMD_RESERVE6: * 0x16 Obsolete
+ * case SCSI_CMD_RELEASE6: * 0x17 Obsolete
+ * case SCSI_CMD_COPY: * 0x18 Obsolete
+ * * 0x19 Vendor specific */
+
+ case SCSI_CMD_MODESENSE6: /* 0x1a Optional */
+ ret = usbmsc_cmdmodesense6(priv, buf);
+ break;
+
+ case SCSI_CMD_STARTSTOPUNIT: /* 0x1b Optional */
+ ret = usbmsc_cmdstartstopunit(priv);
+ break;
+
+ /* case SCSI_CMD_RECEIVEDIAGNOSTICRESULTS: * 0x1c Optional
+ * case SCSI_CMD_SENDDIAGNOSTIC: * 0x1d Mandatory, but not implemented */
+
+ case SCSI_CMD_PREVENTMEDIAREMOVAL: /* 0x1e Optional */
+ ret = usbmsc_cmdpreventmediumremoval(priv);
+ break;
+
+ /* * 0x20-22 Vendor specific */
+ case SCSI_CMD_READFORMATCAPACITIES: /* 0x23 Vendor-specific (defined in MMC spec) */
+ ret = usbmsc_cmdreadformatcapacity(priv, buf);
+ break;
+ /* * 0x24 Vendor specific */
+
+ case SCSI_CMD_READCAPACITY10: /* 0x25 Mandatory */
+ ret = usbmsc_cmdreadcapacity10(priv, buf);
+ break;
+
+ /* * 0x26-27 Vendor specific */
+ case SCSI_CMD_READ10: /* 0x28 Mandatory */
+ ret = usbmsc_cmdread10(priv);
+ break;
+
+ /* * 0x29 Vendor specific */
+
+ case SCSI_CMD_WRITE10: /* 0x2a Optional */
+ ret = usbmsc_cmdwrite10(priv);
+ break;
+
+ /* case SCSI_CMD_SEEK10: * 0x2b Obsolete
+ * * 0x2c-2d Vendor specific
+ * case SCSI_CMD_WRITEANDVERIFY: * 0x2e Optional */
+
+ case SCSI_CMD_VERIFY10: /* 0x2f Optional, but needed my MS Windows */
+ ret = usbmsc_cmdverify10(priv);
+ break;
+
+ /* case SCSI_CMD_SEARCHDATAHIGH: * 0x30 Obsolete
+ * case SCSI_CMD_SEARCHDATAEQUAL: * 0x31 Obsolete
+ * case SCSI_CMD_SEARCHDATALOW: * 0x32 Obsolete
+ * case SCSI_CMD_SETLIMITS10: * 0x33 Obsolete
+ * case SCSI_CMD_PREFETCH10: * 0x34 Optional */
+
+ case SCSI_CMD_SYNCHCACHE10: /* 0x35 Optional */
+ ret = usbmsc_cmdsynchronizecache10(priv);
+ break;
+
+ /* case SCSI_CMD_LOCKCACHE: * 0x36 Obsolete
+ * case SCSI_CMD_READDEFECTDATA10: * 0x37 Optional
+ * case SCSI_CMD_COMPARE: * 0x39 Obsolete
+ * case SCSI_CMD_COPYANDVERIFY: * 0x3a Obsolete
+ * case SCSI_CMD_WRITEBUFFER: * 0x3b Optional
+ * case SCSI_CMD_READBUFFER: * 0x3c Optional
+ * case SCSI_CMD_READLONG10: * 0x3e Optional
+ * case SCSI_CMD_WRITELONG10: * 0x3f Optional
+ * case SCSI_CMD_CHANGEDEFINITION: * 0x40 Obsolete
+ * case SCSI_CMD_WRITESAME10: * 0x41 Optional
+ * case SCSI_CMD_LOGSELECT: * 0x4c Optional
+ * case SCSI_CMD_LOGSENSE: * 0x4d Optional
+ * case SCSI_CMD_XDWRITE10: * 0x50 Optional
+ * case SCSI_CMD_XPWRITE10: * 0x51 Optional
+ * case SCSI_CMD_XDREAD10: * 0x52 Optional */
+
+ case SCSI_CMD_MODESELECT10: /* 0x55 Optional */
+ ret = usbmsc_cmdmodeselect10(priv);
+ break;
+
+ /* case SCSI_CMD_RESERVE10: * 0x56 Obsolete
+ * case SCSI_CMD_RELEASE10: * 0x57 Obsolete */
+
+ case SCSI_CMD_MODESENSE10: /* 0x5a Optional */
+ ret = usbmsc_cmdmodesense10(priv, buf);
+ break;
+
+ /* case SCSI_CMD_PERSISTENTRESERVEIN: * 0x5e Optional
+ * case SCSI_CMD_PERSISTENTRESERVEOUT: * 0x5f Optional
+ * case SCSI_CMD_32: * 0x7f Optional
+ * case SCSI_CMD_XDWRITEEXTENDED: * 0x80 Obsolete
+ * case SCSI_CMD_REBUILD: * 0x81 Obsolete
+ * case SCSI_CMD_REGENERATE: * 0x82 Obsolete
+ * case SCSI_CMD_EXTENDEDCOPY: * 0x83 Optional
+ * case SCSI_CMD_COPYRESULTS: * 0x84 Optional
+ * case SCSI_CMD_ACCESSCONTROLIN: * 0x86 Optional
+ * case SCSI_CMD_ACCESSCONTROLOUT: * 0x87 Optional
+ * case SCSI_CMD_READ16: * 0x88 Optional
+ * case SCSI_CMD_WRITE16: * 0x8a Optional
+ * case SCSI_CMD_READATTRIBUTE: * 0x8c Optional
+ * case SCSI_CMD_WRITEATTRIBUTE: * 0x8d Optional
+ * case SCSI_CMD_WRITEANDVERIFY16: * 0x8e Optional
+ * case SCSI_CMD_SYNCHCACHE16: * 0x91 Optional
+ * case SCSI_CMD_LOCKUNLOCKACACHE: * 0x92 Optional
+ * case SCSI_CMD_WRITESAME16: * 0x93 Optional
+ * case SCSI_CMD_READCAPACITY16: * 0x9e Optional
+ * case SCSI_CMD_READLONG16: * 0x9e Optional
+ * case SCSI_CMD_WRITELONG16 * 0x9f Optional
+ * case SCSI_CMD_REPORTLUNS: * 0xa0 Mandatory, but not implemented
+ * case SCSI_CMD_MAINTENANCEIN: * 0xa3 Optional (SCCS==0)
+ * case SCSI_CMD_MAINTENANCEOUT: * 0xa4 Optional (SCCS==0)
+ * case SCSI_CMD_MOVEMEDIUM: * 0xa5 ?
+ * case SCSI_CMD_MOVEMEDIUMATTACHED: * 0xa7 Optional (MCHNGR==0) */
+
+ case SCSI_CMD_READ12: /* 0xa8 Optional */
+ ret = usbmsc_cmdread12(priv);
+ break;
+
+ case SCSI_CMD_WRITE12: /* 0xaa Optional */
+ ret = usbmsc_cmdwrite12(priv);
+ break;
+
+ /* case SCSI_CMD_READMEDIASERIALNUMBER: * 0xab Optional
+ * case SCSI_CMD_WRITEANDVERIFY12: * 0xae Optional
+ * case SCSI_CMD_VERIFY12: * 0xaf Optional
+ * case SCSI_CMD_SETLIMITS12 * 0xb3 Obsolete
+ * case SCSI_CMD_READELEMENTSTATUS: * 0xb4 Optional (MCHNGR==0)
+ * case SCSI_CMD_READDEFECTDATA12: * 0xb7 Optional
+ * case SCSI_CMD_REDUNDANCYGROUPIN: * 0xba Optional
+ * case SCSI_CMD_REDUNDANCYGROUPOUT: * 0xbb Optional
+ * case SCSI_CMD_SPAREIN: * 0xbc Optional (SCCS==0)
+ * case SCSI_CMD_SPAREOUT: * 0xbd Optional (SCCS==0)
+ * case SCSI_CMD_VOLUMESETIN: * 0xbe Optional (SCCS==0)
+ * case SCSI_CMD_VOLUMESETOUT: * 0xbe Optional (SCCS==0)
+ * * 0xc0-0xff Vendor specific */
+
+ default:
+ priv->u.alloclen = 0;
+ if (ret == OK)
+ {
+ priv->lun->sd = SCSI_KCQIR_INVALIDCOMMAND;
+ ret = -EINVAL;
+ }
+ break;
+ }
+ pthread_mutex_unlock(&priv->mutex);
+
+ /* Is a response required? (Not for read6/10/12 and write6/10/12). */
+
+ if (priv->thstate == USBMSC_STATE_CMDPARSE)
+ {
+ /* All commands come through this path (EXCEPT read6/10/12 and write6/10/12).
+ * For all other commands, the following setup is expected for the response
+ * based on data direction:
+ *
+ * For direction NONE:
+ * 1. priv->u.alloclen == 0
+ * 2. priv->nreqbytes == 0
+ *
+ * For direction = device-to-host:
+ * 1. priv->u.alloclen == allocation length; space set aside by the
+ * host to receive the device data. The size of the response
+ * cannot exceed this value.
+ * 2. response data is in the request currently at the head of
+ * the priv->wrreqlist queue. priv->nreqbytes is set to the length
+ * of data in the response.
+ *
+ * For direction host-to-device
+ * At present, there are no supported commands that should have host-to-device
+ * transfers (except write6/10/12 and that command logic does not take this
+ * path. The 'residue' is left at the full host-to-device data size.
+ *
+ * For all:
+ * ret set to <0 if an error occurred in parsing the commands.
+ */
+
+ /* For from device-to-hose operations, the residue is the expected size
+ * (the initial value of 'residue') minus the amount actually returned
+ * in the response:
+ */
+
+ if (priv->cbwdir == USBMSC_FLAGS_DIRDEVICE2HOST)
+ {
+ /* The number of bytes in the response cannot exceed the host
+ * 'allocation length' in the command.
+ */
+
+ if (priv->nreqbytes > priv->u.alloclen)
+ {
+ priv->nreqbytes = priv->u.alloclen;
+ }
+
+ /* The residue is then the number of bytes that were not sent */
+
+ priv->residue -= priv->nreqbytes;
+ }
+
+ /* On return, we need the following:
+ *
+ * 1. Setup for CMDFINISH state if appropriate
+ * 2. priv->thstate set to either CMDPARSE if no buffer was available or
+ * CMDFINISH to send the response
+ * 3. Return OK to continue; <0 to wait for the next event
+ */
+
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDFINISH), priv->cdb[0]);
+ priv->thstate = USBMSC_STATE_CMDFINISH;
+ ret = OK;
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdreadstate
+ *
+ * Description:
+ * Called from the worker thread in the USBMSC_STATE_CMDREAD state.
+ * The USBMSC_STATE_CMDREAD state is entered when usbmsc_cmdparsestate
+ * obtains a valid SCSI read command. This state is really a continuation
+ * of the USBMSC_STATE_CMDPARSE state that handles extended SCSI read
+ * command handling.
+ *
+ * Returned value:
+ * If no USBDEV write request is available or certain other errors occur, this
+ * function returns a negated errno and stays in the USBMSC_STATE_CMDREAD
+ * state. Otherwise, when the new SCSI read command is fully processed,
+ * this function sets priv->thstate to USBMSC_STATE_CMDFINISH and returns OK.
+ *
+ * State variables:
+ * xfrlen - holds the number of sectors read to be read.
+ * sector - holds the sector number of the next sector to be read
+ * nsectbytes - holds the number of bytes buffered for the current sector
+ * nreqbytes - holds the number of bytes currently buffered in the request
+ * at the head of the wrreqlist.
+ *
+ ****************************************************************************/
+
+static int usbmsc_cmdreadstate(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ FAR struct usbmsc_req_s *privreq;
+ FAR struct usbdev_req_s *req;
+ irqstate_t flags;
+ ssize_t nread;
+ uint8_t *src;
+ uint8_t *dest;
+ int nbytes;
+ int ret;
+
+ /* Loop transferring data until either (1) all of the data has been
+ * transferred, or (2) we have used up all of the write requests that we have
+ * available.
+ */
+
+ while (priv->u.xfrlen > 0 || priv->nsectbytes > 0)
+ {
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDREAD), priv->u.xfrlen);
+
+ /* Is the I/O buffer empty? */
+
+ if (priv->nsectbytes <= 0)
+ {
+ /* Yes.. read the next sector */
+
+ nread = USBMSC_DRVR_READ(lun, priv->iobuffer, priv->sector, 1);
+ if (nread < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDREADREADFAIL), -nread);
+ lun->sd = SCSI_KCQME_UNRRE1;
+ lun->sdinfo = priv->sector;
+ break;
+ }
+
+ priv->nsectbytes = lun->sectorsize;
+ priv->u.xfrlen--;
+ priv->sector++;
+ }
+
+ /* Check if there is a request in the wrreqlist that we will be able to
+ * use for data transfer.
+ */
+
+ privreq = (FAR struct usbmsc_req_s *)sq_peek(&priv->wrreqlist);
+
+ /* If there no request structures available, then just return an error.
+ * This will cause us to remain in the CMDREAD state. When a request is
+ * returned, the worker thread will be awakened in the USBMSC_STATE_CMDREAD
+ * and we will be called again.
+ */
+
+ if (!privreq)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDREADWRRQEMPTY), 0);
+ priv->nreqbytes = 0;
+ return -ENOMEM;
+ }
+ req = privreq->req;
+
+ /* Transfer all of the data that will (1) fit into the request buffer, OR (2)
+ * all of the data available in the sector buffer.
+ */
+
+ src = &priv->iobuffer[lun->sectorsize - priv->nsectbytes];
+ dest = &req->buf[priv->nreqbytes];
+
+ nbytes = MIN(CONFIG_USBMSC_BULKINREQLEN - priv->nreqbytes, priv->nsectbytes);
+
+ /* Copy the data from the sector buffer to the USB request and update counts */
+
+ memcpy(dest, src, nbytes);
+ priv->nreqbytes += nbytes;
+ priv->nsectbytes -= nbytes;
+
+ /* If (1) the request buffer is full OR (2) this is the final request full of data,
+ * then submit the request
+ */
+
+ if (priv->nreqbytes >= CONFIG_USBMSC_BULKINREQLEN ||
+ (priv->u.xfrlen <= 0 && priv->nsectbytes <= 0))
+ {
+ /* Remove the request that we just filled from wrreqlist (we've already checked
+ * that is it not NULL
+ */
+
+ flags = irqsave();
+ privreq = (FAR struct usbmsc_req_s *)sq_remfirst(&priv->wrreqlist);
+ irqrestore(flags);
+
+ /* And submit the request to the bulk IN endpoint */
+
+ req->len = priv->nreqbytes;
+ req->priv = privreq;
+ req->callback = usbmsc_wrcomplete;
+ req->flags = 0;
+
+ ret = EP_SUBMIT(priv->epbulkin, req);
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDREADSUBMIT), (uint16_t)-ret);
+ lun->sd = SCSI_KCQME_UNRRE1;
+ lun->sdinfo = priv->sector;
+ break;
+ }
+
+ /* Assume success... residue should probably really be decremented in
+ * wrcomplete when we know that the transfer completed successfully.
+ */
+
+ priv->residue -= priv->nreqbytes;
+ priv->nreqbytes = 0;
+ }
+ }
+
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDREADCMDFINISH), priv->u.xfrlen);
+ priv->thstate = USBMSC_STATE_CMDFINISH;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdwritestate
+ *
+ * Description:
+ * Called from the worker thread in the USBMSC_STATE_CMDWRITE state.
+ * The USBMSC_STATE_CMDWRITE state is entered when usbmsc_cmdparsestate
+ * obtains a valid SCSI write command. This state is really a continuation
+ * of the USBMSC_STATE_CMDPARSE state that handles extended SCSI write
+ * command handling.
+ *
+ * Returned value:
+ * If no USBDEV write request is available or certain other errors occur, this
+ * function returns a negated errno and stays in the USBMSC_STATE_CMDWRITE
+ * state. Otherwise, when the new SCSI write command is fully processed,
+ * this function sets priv->thstate to USBMSC_STATE_CMDFINISH and returns OK.
+ *
+ * State variables:
+ * xfrlen - holds the number of sectors read to be written.
+ * sector - holds the sector number of the next sector to write
+ * nsectbytes - holds the number of bytes buffered for the current sector
+ * nreqbytes - holds the number of untransferred bytes currently in the
+ * request at the head of the rdreqlist.
+ *
+ ****************************************************************************/
+
+static int usbmsc_cmdwritestate(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ FAR struct usbmsc_req_s *privreq;
+ FAR struct usbdev_req_s *req;
+ ssize_t nwritten;
+ uint16_t xfrd;
+ uint8_t *src;
+ uint8_t *dest;
+ int nbytes;
+ int ret;
+
+ /* Loop transferring data until either (1) all of the data has been
+ * transferred, or (2) we have written all of the data in the available
+ * read requests.
+ */
+
+ while (priv->u.xfrlen > 0)
+ {
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDWRITE), priv->u.xfrlen);
+
+ /* Check if there is a request in the rdreqlist containing additional
+ * data to be written.
+ */
+
+ privreq = (FAR struct usbmsc_req_s *)sq_remfirst(&priv->rdreqlist);
+
+ /* If there no request data available, then just return an error.
+ * This will cause us to remain in the CMDWRITE state. When a filled request is
+ * received, the worker thread will be awakened in the USBMSC_STATE_CMDWRITE
+ * and we will be called again.
+ */
+
+ if (!privreq)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDWRITERDRQEMPTY), 0);
+ priv->nreqbytes = 0;
+ return -ENOMEM;
+ }
+
+ req = privreq->req;
+ xfrd = req->xfrd;
+ priv->nreqbytes = xfrd;
+
+ /* Now loop until all of the data in the read request has been tranferred
+ * to the block driver OR all of the request data has been transferred.
+ */
+
+ while (priv->nreqbytes > 0 && priv->u.xfrlen > 0)
+ {
+ /* Copy the data received in the read request into the sector I/O buffer */
+
+ src = &req->buf[xfrd - priv->nreqbytes];
+ dest = &priv->iobuffer[priv->nsectbytes];
+
+ nbytes = MIN(lun->sectorsize - priv->nsectbytes, priv->nreqbytes);
+
+ /* Copy the data from the sector buffer to the USB request and update counts */
+
+ memcpy(dest, src, nbytes);
+ priv->nsectbytes += nbytes;
+ priv->nreqbytes -= nbytes;
+
+ /* Is the I/O buffer full? */
+
+ if (priv->nsectbytes >= lun->sectorsize)
+ {
+ /* Yes.. Write the next sector */
+
+ nwritten = USBMSC_DRVR_WRITE(lun, priv->iobuffer, priv->sector, 1);
+ if (nwritten < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDWRITEWRITEFAIL), -nwritten);
+ lun->sd = SCSI_KCQME_WRITEFAULTAUTOREALLOCFAILED;
+ lun->sdinfo = priv->sector;
+ goto errout;
+ }
+
+ priv->nsectbytes = 0;
+ priv->residue -= lun->sectorsize;
+ priv->u.xfrlen--;
+ priv->sector++;
+ }
+ }
+
+ /* In either case, we are finished with this read request and can return it
+ * to the endpoint. Then we will go back to the top of the top and attempt
+ * to get the next read request.
+ */
+
+ req->len = CONFIG_USBMSC_BULKOUTREQLEN;
+ req->priv = privreq;
+ req->callback = usbmsc_rdcomplete;
+
+ ret = EP_SUBMIT(priv->epbulkout, req);
+ if (ret != OK)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDWRITERDSUBMIT), (uint16_t)-ret);
+ }
+
+ /* Did the host decide to stop early? */
+
+ if (xfrd != CONFIG_USBMSC_BULKOUTREQLEN)
+ {
+ priv->shortpacket = 1;
+ goto errout;
+ }
+ }
+
+errout:
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDWRITECMDFINISH), priv->u.xfrlen);
+ priv->thstate = USBMSC_STATE_CMDFINISH;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdfinishstate
+ *
+ * Description:
+ * Called from the worker thread in the USBMSC_STATE_CMDFINISH state.
+ * The USBMSC_STATE_CMDFINISH state is entered when processing of a
+ * command has finished but before status has been returned.
+ *
+ * Returned value:
+ * If no USBDEV write request is available or certain other errors occur, this
+ * function returns a negated errno and stays in the USBMSC_STATE_CMDFINISH
+ * state. Otherwise, when the command is fully processed, this function
+ * sets priv->thstate to USBMSC_STATE_CMDSTATUS and returns OK.
+ *
+ ****************************************************************************/
+
+static int usbmsc_cmdfinishstate(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct usbmsc_req_s *privreq;
+ irqstate_t flags;
+ int ret = OK;
+
+ /* Check if there is a request in the wrreqlist that we will be able to
+ * use for data transfer.
+ */
+
+ privreq = (FAR struct usbmsc_req_s *)sq_peek(&priv->wrreqlist);
+
+ /* If there no request structures available, then just return an error.
+ * This will cause us to remain in the CMDREAD state. When a request is
+ * returned, the worker thread will be awakened in the USBMSC_STATE_CMDREAD
+ * and we will be called again.
+ */
+
+ if (!privreq)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDFINISHRQEMPTY), 0);
+ return -ENOMEM;
+ }
+
+ /* Finish the final stages of the reply */
+
+ switch (priv->cbwdir)
+ {
+ /* Device-to-host: All but the last data buffer has been sent */
+
+ case USBMSC_FLAGS_DIRDEVICE2HOST:
+ if (priv->cbwlen > 0)
+ {
+ /* On most commands (the exception is outgoing, write commands),
+ * the data has not not yet been sent.
+ */
+
+ if (priv->nreqbytes > 0)
+ {
+ struct usbdev_req_s *req;
+
+ /* Take a request from the wrreqlist (we've already checked
+ * that is it not NULL)
+ */
+
+ flags = irqsave();
+ privreq = (FAR struct usbmsc_req_s *)sq_remfirst(&priv->wrreqlist);
+ irqrestore(flags);
+
+ /* Send the write request */
+
+ req = privreq->req;
+ req->len = priv->nreqbytes;
+ req->callback = usbmsc_wrcomplete;
+ req->priv = privreq;
+ req->flags = USBDEV_REQFLAGS_NULLPKT;
+
+ ret = EP_SUBMIT(priv->epbulkin, privreq->req);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDFINISHSUBMIT), (uint16_t)-ret);
+ }
+ }
+
+ /* Stall the BULK In endpoint if there is a residue */
+
+ if (priv->residue > 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDFINISHRESIDUE), (uint16_t)priv->residue);
+
+#ifdef CONFIG_USBMSC_RACEWAR
+ /* (See description of the workaround at the top of the file).
+ * First, wait for the transfer to complete, then stall the endpoint
+ */
+
+ usleep (100000);
+ (void)EP_STALL(priv->epbulkin);
+
+ /* now wait for stall to go away .... */
+
+ usleep (100000);
+#else
+ (void)EP_STALL(priv->epbulkin);
+#endif
+ }
+ }
+ break;
+
+ /* Host-to-device: We have processed all we want of the host data. */
+
+ case USBMSC_FLAGS_DIRHOST2DEVICE:
+ if (priv->residue > 0)
+ {
+ /* Did the host stop sending unexpectedly early? */
+
+ flags = irqsave();
+ if (priv->shortpacket)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDFINISHSHORTPKT), (uint16_t)priv->residue);
+ }
+
+ /* Unprocessed incoming data: STALL and cancel requests. */
+
+ else
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDFINSHSUBMIT), (uint16_t)priv->residue);
+ EP_STALL(priv->epbulkout);
+ }
+
+ priv->theventset |= USBMSC_EVENT_ABORTBULKOUT;
+ irqrestore(flags);
+ }
+ break;
+
+ default:
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDFINSHDIR), priv->cbwdir);
+ case USBMSC_FLAGS_DIRNONE: /* Nothing to send */
+ break;
+ }
+
+ /* Return to the IDLE state */
+
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDFINISHCMDSTATUS), 0);
+ priv->thstate = USBMSC_STATE_CMDSTATUS;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbmsc_cmdstatusstate
+ *
+ * Description:
+ * Called from the worker thread in the USBMSC_STATE_CMDSTATUS state.
+ * That state is after a CBW has been fully processed. This function sends
+ * the concluding CSW.
+ *
+ * Returned value:
+ * If no write request is available or certain other errors occur, this
+ * function returns a negated errno and stays in the USBMSC_STATE_CMDSTATUS
+ * state. Otherwise, when the SCSI statis is successfully returned, this
+ * function sets priv->thstate to USBMSC_STATE_IDLE and returns OK.
+ *
+ ****************************************************************************/
+
+static int usbmsc_cmdstatusstate(FAR struct usbmsc_dev_s *priv)
+{
+ FAR struct usbmsc_lun_s *lun = priv->lun;
+ FAR struct usbmsc_req_s *privreq;
+ FAR struct usbdev_req_s *req;
+ FAR struct usbmsc_csw_s *csw;
+ irqstate_t flags;
+ uint32_t sd;
+ uint8_t status = USBMSC_CSWSTATUS_PASS;
+ int ret;
+
+ /* Take a request from the wrreqlist */
+
+ flags = irqsave();
+ privreq = (FAR struct usbmsc_req_s *)sq_remfirst(&priv->wrreqlist);
+ irqrestore(flags);
+
+ /* If there no request structures available, then just return an error.
+ * This will cause us to remain in the CMDSTATUS status. When a request is
+ * returned, the worker thread will be awakened in the USBMSC_STATE_CMDSTATUS
+ * and we will be called again.
+ */
+
+ if (!privreq)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_CMDSTATUSRDREQLISTEMPTY), 0);
+ return -ENOMEM;
+ }
+
+ req = privreq->req;
+ csw = (struct usbmsc_csw_s*)req->buf;
+
+ /* Extract the sense data from the LUN structure */
+
+ if (lun)
+ {
+ sd = lun->sd;
+ }
+ else
+ {
+ sd = SCSI_KCQIR_INVALIDLUN;
+ }
+
+ /* Determine the CSW status: PASS, PHASEERROR, or FAIL */
+
+ if (priv->phaseerror)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_SNDPHERROR), 0);
+ status = USBMSC_CSWSTATUS_PHASEERROR;
+ sd = SCSI_KCQIR_INVALIDCOMMAND;
+ }
+ else if (sd != SCSI_KCQ_NOSENSE)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_SNDCSWFAIL), 0);
+ status = USBMSC_CSWSTATUS_FAIL;
+ }
+
+ /* Format and submit the CSW */
+
+ csw->signature[0] = 'U';
+ csw->signature[1] = 'S';
+ csw->signature[2] = 'B';
+ csw->signature[3] = 'S';
+ usbmsc_putle32(csw->tag, priv->cbwtag);
+ usbmsc_putle32(csw->residue, priv->residue);
+ csw->status = status;
+
+ usbmsc_dumpdata("SCSCI CSW", (uint8_t*)csw, USBMSC_CSW_SIZEOF);
+
+ req->len = USBMSC_CSW_SIZEOF;
+ req->callback = usbmsc_wrcomplete;
+ req->priv = privreq;
+ req->flags = USBDEV_REQFLAGS_NULLPKT;
+
+ ret = EP_SUBMIT(priv->epbulkin, req);
+ if (ret < 0)
+ {
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_SNDSTATUSSUBMIT), (uint16_t)-ret);
+ flags = irqsave();
+ (void)sq_addlast((sq_entry_t*)privreq, &priv->wrreqlist);
+ irqrestore(flags);
+ }
+
+ /* Return to the IDLE state */
+
+ usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDSTATUSIDLE), 0);
+ priv->thstate = USBMSC_STATE_IDLE;
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbmsc_workerthread
+ *
+ * Description:
+ * This is the main function of the USB storage worker thread. It loops
+ * until USB-related events occur, then processes those events accordingly
+ *
+ ****************************************************************************/
+
+void *usbmsc_workerthread(void *arg)
+{
+ struct usbmsc_dev_s *priv = (struct usbmsc_dev_s *)arg;
+ irqstate_t flags;
+ uint16_t eventset;
+ int ret;
+
+ /* This thread is started before the USB storage class is fully initialized.
+ * wait here until we are told to begin. Start in the NOTINITIALIZED state
+ */
+
+ pthread_mutex_lock(&priv->mutex);
+ priv->thstate = USBMSC_STATE_STARTED;
+ while ((priv->theventset & USBMSC_EVENT_READY) != 0 &&
+ (priv->theventset & USBMSC_EVENT_TERMINATEREQUEST) != 0)
+ {
+ pthread_cond_wait(&priv->cond, &priv->mutex);
+ }
+
+ /* Transition to the INITIALIZED/IDLE state */
+
+ priv->thstate = USBMSC_STATE_IDLE;
+ eventset = priv->theventset;
+ priv->theventset = USBMSC_EVENT_NOEVENTS;
+ pthread_mutex_unlock(&priv->mutex);
+
+ /* Then loop until we are asked to terminate */
+
+ while ((eventset & USBMSC_EVENT_TERMINATEREQUEST) == 0)
+ {
+ /* Wait for some interesting event. Note that we must both take the
+ * lock (to eliminate race conditions with other threads) and disable
+ * interrupts (to eliminate race conditions with USB interrupt handling.
+ */
+
+ pthread_mutex_lock(&priv->mutex);
+ flags = irqsave();
+ if (priv->theventset == USBMSC_EVENT_NOEVENTS)
+ {
+ pthread_cond_wait(&priv->cond, &priv->mutex);
+ }
+
+ /* Sample any events before re-enabling interrupts. Any events that
+ * occur after re-enabling interrupts will have to be handled in the
+ * next time through the loop.
+ */
+
+ eventset = priv->theventset;
+ priv->theventset = USBMSC_EVENT_NOEVENTS;
+ pthread_mutex_unlock(&priv->mutex);
+
+ /* Were we awakened by some event that requires immediate action?
+ *
+ * - The USBMSC_EVENT_DISCONNECT is signalled from the disconnect method
+ * after all transfers have been stopped, when the host is disconnected.
+ *
+ * - The CUSBMSC_EVENT_RESET is signalled when the bulk-storage-specific
+ * USBMSC_REQ_MSRESET EP0 setup received. We must stop the current
+ * operation and reinialize state.
+ *
+ * - The USBMSC_EVENT_CFGCHANGE is signaled when the EP0 setup logic
+ * receives a valid USB_REQ_SETCONFIGURATION request
+ *
+ * - The USBMSC_EVENT_IFCHANGE is signaled when the EP0 setup logic
+ * receives a valid USB_REQ_SETINTERFACE request
+ *
+ * - The USBMSC_EVENT_ABORTBULKOUT event is signalled by the CMDFINISH
+ * logic when there is a residue after processing a host-to-device
+ * transfer. We need to discard all incoming request.
+ *
+ * All other events are just wakeup calls and are intended only
+ * drive the state machine.
+ */
+
+ if ((eventset & (USBMSC_EVENT_DISCONNECT|USBMSC_EVENT_RESET|USBMSC_EVENT_CFGCHANGE|
+ USBMSC_EVENT_IFCHANGE|USBMSC_EVENT_ABORTBULKOUT)) != 0)
+ {
+ /* These events require that the current configuration be reset */
+
+ if ((eventset & USBMSC_EVENT_IFCHANGE) != 0)
+ {
+ usbmsc_resetconfig(priv);
+ }
+
+ /* These events require that a new configuration be established */
+
+ if ((eventset & (USBMSC_EVENT_CFGCHANGE|USBMSC_EVENT_IFCHANGE)) != 0)
+ {
+ usbmsc_setconfig(priv, priv->thvalue);
+ }
+
+ /* These events required that we send a deferred EP0 setup response */
+
+ if ((eventset & (USBMSC_EVENT_RESET|USBMSC_EVENT_CFGCHANGE|USBMSC_EVENT_IFCHANGE)) != 0)
+ {
+ usbmsc_deferredresponse(priv, false);
+ }
+
+ /* For all of these events... terminate any transactions in progress */
+
+ priv->thstate = USBMSC_STATE_IDLE;
+ }
+ irqrestore(flags);
+
+ /* Loop processing each SCSI command state. Each state handling
+ * function will do the following:
+ *
+ * - If it must block for an event, it will return a negated errno value
+ * - If it completes the processing for that state, it will (1) set
+ * the next appropriate state value and (2) return OK.
+ *
+ * So the following will loop until we must block for an event in
+ * a particular state. When we are awakened by an event (above) we
+ * will resume processing in the same state.
+ */
+
+ do
+ {
+ switch (priv->thstate)
+ {
+ case USBMSC_STATE_IDLE: /* Started and waiting for commands */
+ ret = usbmsc_idlestate(priv);
+ break;
+
+ case USBMSC_STATE_CMDPARSE: /* Parsing the received a command */
+ ret = usbmsc_cmdparsestate(priv);
+ break;
+
+ case USBMSC_STATE_CMDREAD: /* Continuing to process a SCSI read command */
+ ret = usbmsc_cmdreadstate(priv);
+ break;
+
+ case USBMSC_STATE_CMDWRITE: /* Continuing to process a SCSI write command */
+ ret = usbmsc_cmdwritestate(priv);
+ break;
+
+ case USBMSC_STATE_CMDFINISH: /* Finish command processing */
+ ret = usbmsc_cmdfinishstate(priv);
+ break;
+
+ case USBMSC_STATE_CMDSTATUS: /* Processing the status phase of a command */
+ ret = usbmsc_cmdstatusstate(priv);
+ break;
+
+ case USBMSC_STATE_NOTSTARTED: /* Thread has not yet been started */
+ case USBMSC_STATE_STARTED: /* Started, but is not yet initialized */
+ case USBMSC_STATE_TERMINATED: /* Thread has exitted */
+ default:
+ usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_INVALIDSTATE), priv->thstate);
+ priv->thstate = USBMSC_STATE_IDLE;
+ ret = OK;
+ break;
+ }
+ }
+ while (ret == OK);
+ }
+
+ /* Transition to the TERMINATED state and exit */
+
+ priv->thstate = USBMSC_STATE_TERMINATED;
+ return NULL;
+}
diff --git a/nuttx/drivers/usbhost/Kconfig b/nuttx/drivers/usbhost/Kconfig
new file mode 100644
index 000000000..35695d750
--- /dev/null
+++ b/nuttx/drivers/usbhost/Kconfig
@@ -0,0 +1,92 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+config USBHOST_NPREALLOC
+ int "Number of pre-allocated class instances"
+ default 4
+ ---help---
+ Number of pre-allocated class instances
+
+config USBHOST_BULK_DISABLE
+ bool "Disable bulk endpoint support"
+ default n
+ ---help---
+ On some architectures, selecting this setting will reduce driver size
+ by disabling bulk endpoint support
+
+config USBHOST_INT_DISABLE
+ bool "Disable interrupt endpoint support"
+ default n
+ ---help---
+ On some architectures, selecting this setting will reduce driver size
+ by disabling interrupt endpoint support
+
+config USBHOST_ISOC_DISABLE
+ bool "Disable isochronous endpoint support"
+ default n
+ ---help---
+ On some architectures, selecting this setting will reduce driver size
+ by disabling isochronous endpoint support
+
+config USBHOST_HIDKBD
+ bool "HID keyboad class support"
+ default n
+ depends on !USBHOST_INT_DISABLE && SCHED_WORKQUEUE && !DISABLE_SIGNALS
+
+if USBHOST_HIDKBD
+config HIDKBD_POLLUSEC
+ bool ""
+ default n
+ ---help---
+ Device poll rate in microseconds. Default: 100 milliseconds.
+
+config HIDKBD_DEFPRIO
+ bool ""
+ default n
+ ---help---
+ Priority of the polling thread. Default: 50.
+
+config HIDKBD_STACKSIZE
+ bool ""
+ default n
+ ---help---
+ Stack size for polling thread. Default: 1024
+
+config HIDKBD_BUFSIZE
+ bool ""
+ default n
+ ---help---
+ Scancode buffer size. Default: 64.
+
+config HIDKBD_NPOLLWAITERS
+ bool ""
+ default n
+ ---help---
+ If the poll() method is enabled, this defines the maximum number
+ of threads that can be waiting for keyboard events. Default: 2.
+
+config HIDKBD_RAWSCANCODES
+ bool ""
+ default n
+ ---help---
+ If set to y no conversion will be made on the raw keyboard scan
+ codes. Default: ASCII conversion.
+
+config HIDKBD_ALLSCANCODES
+ bool ""
+ default n
+ ---help---
+ If set to y all 231 possible scancodes will be converted to
+ something. Default: 104 key US keyboard.
+
+config HIDKBD_NODEBOUNCE
+ bool ""
+ default n
+ ---help---
+ If set to y normal debouncing is disabled. Default:
+ Debounce enabled (No repeat keys).
+ USB host mass storage class driver. Requires USBHOST=y,
+ config USBHOST_BULK_DISABLE=n, NFILE_DESCRIPTORS > 0,
+ and SCHED_WORKQUEUE=y
+endif
diff --git a/nuttx/drivers/usbhost/Make.defs b/nuttx/drivers/usbhost/Make.defs
new file mode 100644
index 000000000..fd54ab53e
--- /dev/null
+++ b/nuttx/drivers/usbhost/Make.defs
@@ -0,0 +1,57 @@
+############################################################################
+# drivers/usbhost/Make.defs
+#
+# Copyright (C) 2010-2011 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+CSRCS += hid_parser.c
+
+ifeq ($(CONFIG_USBHOST),y)
+
+# Include built-in USB host driver logic
+
+CSRCS += usbhost_registry.c usbhost_registerclass.c usbhost_findclass.c
+CSRCS += usbhost_enumerate.c usbhost_storage.c usbhost_hidkbd.c
+
+# Include add-on USB host driver logic (see misc/drivers)
+
+ifeq ($(CONFIG_NET),y)
+ RTL8187_CSRC := ${shell if [ -f usbhost/rtl8187x.c ]; then echo "rtl8187x.c"; fi}
+ CSRCS += $(RTL8187_CSRC)
+endif
+endif
+
+# Include USB host driver build logic
+
+DEPPATH += --dep-path usbhost
+VPATH += :usbhost
+CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/usbhost}
diff --git a/nuttx/drivers/usbhost/hid_parser.c b/nuttx/drivers/usbhost/hid_parser.c
new file mode 100644
index 000000000..a0ca6066b
--- /dev/null
+++ b/nuttx/drivers/usbhost/hid_parser.c
@@ -0,0 +1,529 @@
+/****************************************************************************
+ * drivers/usbhost/hid_parser.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ *
+ * Adapted from the LUFA Library:
+ *
+ * Copyright 2011 Dean Camera (dean [at] fourwalledcubicle [dot] com)
+ * dean [at] fourwalledcubicle [dot] com, www.lufa-lib.org
+ *
+ * Permission to use, copy, modify, distribute, and sell this
+ * software and its documentation for any purpose is hereby granted
+ * without fee, provided that the above copyright notice appear in
+ * all copies and that both that the copyright notice and this
+ * permission notice and warranty disclaimer appear in supporting
+ * documentation, and that the name of the author not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * The author disclaim all warranties with regard to this
+ * software, including all implied warranties of merchantability
+ * and fitness. In no event shall the author be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether
+ * in an action of contract, negligence or other tortious action,
+ * arising out of or in connection with the use or performance of
+ * this software.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/usb/hid.h>
+#include <nuttx/usb/hid_parser.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct hid_state_s
+{
+ struct hid_rptitem_attributes_s attrib;
+ uint8_t rptcount;
+ uint8_t id;
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: hid_parsereport
+ *
+ * Description:
+ * Function to process a given HID report returned from an attached device,
+ * and store it into a given struct hid_rptinfo_s structure.
+ *
+ * Input Parameters:
+ * report Buffer containing the device's HID report table.
+ * rptlen Size in bytes of the HID report table.
+ * filter Callback function to decide if an item should be retained
+ * rptinfo Pointer to a struct hid_rptinfo_s instance for the parser output.
+ *
+ * Returned Value:
+ * Zero on success, otherwise a negated errno value.
+ ****************************************************************************/
+
+int hid_parsereport(FAR const uint8_t *report, int rptlen,
+ hid_rptfilter_t filter, FAR struct hid_rptinfo_s *rptinfo)
+{
+ struct hid_state_s state[CONFIG_HID_STATEDEPTH];
+ struct hid_state_s *currstate = &state[0];
+ struct hid_collectionpath_s *collectionpath = NULL;
+ struct hid_rptsizeinfo_s *rptidinfo = &rptinfo->rptsize[0];
+ uint16_t usage[CONFIG_HID_USAGEDEPTH];
+ uint8_t nusage = 0;
+ struct hid_range_s usage_range = { 0, 0 };
+ int i;
+
+ DEBUGASSERT(report && filter && rptinfo);
+
+ memset(rptinfo, 0x00, sizeof(struct hid_rptinfo_s));
+ memset(currstate, 0x00, sizeof(struct hid_state_s));
+ memset(rptidinfo, 0x00, sizeof(struct hid_rptsizeinfo_s));
+
+ rptinfo->nreports = 1;
+
+ while (rptlen > 0)
+ {
+ uint8_t item = *report;
+ uint32_t data = 0;
+
+ report++;
+ rptlen--;
+
+ switch (item & USBHID_RPTITEM_SIZE_MASK)
+ {
+ case USBHID_RPTITEM_SIZE_4: /* 4 bytes of little endian data follow */
+ data = (uint32_t)(*report++);
+ data |= (uint32_t)(*report++) << 8;
+ data |= (uint32_t)(*report++) << 16;
+ data |= (uint32_t)(*report++) << 24;
+ rptlen -= 4;
+ break;
+
+ case USBHID_RPTITEM_SIZE_2: /* 2 bytes of little endian data follow */
+ data = (uint32_t)(*report++);
+ data |= (uint32_t)(*report++) << 8;
+ rptlen -= 2;
+ break;
+
+ case USBHID_RPTITEM_SIZE_1: /* 1 byte of data follows */
+ data = (uint32_t)(*report++);
+ rptlen -= 1;
+ break;
+
+ case USBHID_RPTITEM_SIZE_0: /* No data follows */
+ default:
+ break;
+ }
+
+ switch (item & ~USBHID_RPTITEM_SIZE_MASK)
+ {
+ case USBHID_GLOBAL_PUSH_PREFIX:
+ if (currstate == &state[CONFIG_HID_STATEDEPTH - 1])
+ {
+ return -E2BIG;
+ }
+
+ memcpy((currstate + 1),
+ currstate, sizeof(struct hid_rptitem_s));
+
+ currstate++;
+ break;
+
+ case USBHID_GLOBAL_POP_PREFIX:
+ if (currstate == &state[0])
+ {
+ return -EINVAL; /* Pop without push? */
+ }
+
+ currstate--;
+ break;
+
+ case USBHID_GLOBAL_USAGEPAGE_PREFIX:
+ if ((item & USBHID_RPTITEM_SIZE_MASK) == USBHID_RPTITEM_SIZE_4)
+ {
+ currstate->attrib.usage.page = (data >> 16);
+ }
+
+ currstate->attrib.usage.page = data;
+ break;
+
+ case USBHID_GLOBAL_LOGICALMIN_PREFIX:
+ currstate->attrib.logical.min = data;
+ break;
+
+ case USBHID_GLOBAL_LOGICALMAX_PREFIX:
+ currstate->attrib.logical.max = data;
+ break;
+
+ case USBHID_GLOBAL_PHYSICALMIN_PREFIX:
+ currstate->attrib.physical.min = data;
+ break;
+
+ case USBHID_GLOBAL_PHYSMICALAX_PREFIX:
+ currstate->attrib.physical.max = data;
+ break;
+
+ case USBHID_GLOBAL_UNITEXP_PREFIX:
+ currstate->attrib.unit.exponent = data;
+ break;
+
+ case USBHID_GLOBAL_UNIT_PREFIX:
+ currstate->attrib.unit.type = data;
+ break;
+
+ case USBHID_GLOBAL_REPORTSIZE_PREFIX:
+ currstate->attrib.bitsize = data;
+ break;
+
+ case USBHID_GLOBAL_REPORTCOUNT_PREFIX:
+ currstate->rptcount = data;
+ break;
+
+ case USBHID_GLOBAL_REPORTID_PREFIX:
+ currstate->id = data;
+
+ if (rptinfo->haverptid)
+ {
+ rptidinfo = NULL;
+
+ for (i = 0; i < rptinfo->nreports; i++)
+ {
+ if (rptinfo->rptsize[i].id == currstate->id)
+ {
+ rptidinfo = &rptinfo->rptsize[i];
+ break;
+ }
+ }
+
+ if (rptidinfo == NULL)
+ {
+ if (rptinfo->nreports == CONFIG_HID_MAXIDS)
+ {
+ return -EINVAL;
+ }
+
+ rptidinfo = &rptinfo->rptsize[rptinfo->nreports++];
+ memset(rptidinfo, 0x00, sizeof(struct hid_rptsizeinfo_s));
+ }
+ }
+
+ rptinfo->haverptid = true;
+
+ rptidinfo->id = currstate->id;
+ break;
+
+ case USBHID_LOCAL_USAGE_PREFIX:
+ if (nusage == CONFIG_HID_USAGEDEPTH)
+ {
+ return -E2BIG;
+ }
+
+ usage[nusage++] = data;
+ break;
+
+ case USBHID_LOCAL_USAGEMIN_PREFIX:
+ usage_range.min = data;
+ break;
+
+ case USBHID_LOCAL_USAGEMAX_PREFIX:
+ usage_range.max = data;
+ break;
+
+ case USBHID_MAIN_COLLECTION_PREFIX:
+ if (collectionpath == NULL)
+ {
+ collectionpath = &rptinfo->collectionpaths[0];
+ }
+ else
+ {
+ struct hid_collectionpath_s *ParentCollectionPath = collectionpath;
+
+ collectionpath = &rptinfo->collectionpaths[1];
+
+ while (collectionpath->parent != NULL)
+ {
+ if (collectionpath == &rptinfo->collectionpaths[CONFIG_HID_MAXCOLLECTIONS - 1])
+ {
+ return -EINVAL;
+ }
+
+ collectionpath++;
+ }
+
+ collectionpath->parent = ParentCollectionPath;
+ }
+
+ collectionpath->type = data;
+ collectionpath->usage.page = currstate->attrib.usage.page;
+
+ if (nusage)
+ {
+ collectionpath->usage.usage = usage[0];
+
+ for (i = 0; i < nusage; i++)
+ usage[i] = usage[i + 1];
+
+ nusage--;
+ }
+ else if (usage_range.min <= usage_range.max)
+ {
+ collectionpath->usage.usage = usage_range.min++;
+ }
+
+ break;
+
+ case USBHID_MAIN_ENDCOLLECTION_PREFIX:
+ if (collectionpath == NULL)
+ {
+ return -EINVAL;
+ }
+
+ collectionpath = collectionpath->parent;
+ break;
+
+ case USBHID_MAIN_INPUT_PREFIX:
+ case USBHID_MAIN_OUTPUT_PREFIX:
+ case USBHID_MAIN_FEATURE_PREFIX:
+ {
+ int itemno;
+ for (itemno = 0; itemno < currstate->rptcount; itemno++)
+ {
+ struct hid_rptitem_s newitem;
+ uint8_t tag;
+
+ memcpy(&newitem.attrib, &currstate->attrib,
+ sizeof(struct hid_rptitem_attributes_s));
+
+ newitem.flags = data;
+ newitem.collectionpath = collectionpath;
+ newitem.id = currstate->id;
+
+ if (nusage)
+ {
+ newitem.attrib.usage.usage = usage[0];
+
+ for (i = 0; i < nusage; i++)
+ {
+ usage[i] = usage[i + 1];
+ }
+ nusage--;
+ }
+ else if (usage_range.min <= usage_range.max)
+ {
+ newitem.attrib.usage.usage = usage_range.min++;
+ }
+
+ tag = (item & ~USBHID_RPTITEM_SIZE_MASK);
+ if (tag == USBHID_MAIN_INPUT_PREFIX)
+ {
+ newitem.type = HID_REPORT_ITEM_IN;
+ }
+ else if (tag == USBHID_MAIN_OUTPUT_PREFIX)
+ {
+ newitem.type = HID_REPORT_ITEM_OUT;
+ }
+ else
+ {
+ newitem.type = HID_REPORT_ITEM_FEATURE;
+ }
+
+ newitem.bitoffset = rptidinfo->size[newitem.type];
+ rptidinfo->size[newitem.type] += currstate->attrib.bitsize;
+
+ /* Accumulate the maximum report size */
+
+ if (rptinfo->maxrptsize < newitem.bitoffset)
+ {
+ rptinfo->maxrptsize = newitem.bitoffset;
+ }
+
+ if ((data & USBHID_MAIN_CONSTANT) == 0 && filter(&newitem))
+ {
+ if (rptinfo->nitems == CONFIG_HID_MAXITEMS)
+ {
+ return -EINVAL;
+ }
+
+ memcpy(&rptinfo->items[rptinfo->nitems],
+ &newitem, sizeof(struct hid_rptitem_s));
+
+ rptinfo->nitems++;
+ }
+ }
+ }
+ break;
+ }
+
+ if ((item & USBHID_RPTITEM_TYPE_MASK) == USBHID_RPTITEM_TYPE_MAIN)
+ {
+ usage_range.min = 0;
+ usage_range.max = 0;
+ nusage = 0;
+ }
+ }
+
+ if (!(rptinfo->nitems))
+ {
+ return -ENOENT;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: hid_getitem
+ *
+ * Description:
+ * Extracts the given report item's value out of the given HID report and
+ * places it into the value member of the report item's struct hid_rptitem_s
+ * structure.
+ *
+ * When called on a report with an item that exists in that report, this
+ * copies the report item's Value to it's previous element for easy
+ * checking to see if an item's value has changed before processing a
+ * report. If the given item does not exist in the report, the function
+ * does not modify the report item's data.
+ *
+ * Input Parameters
+ * report Buffer containing an IN or FEATURE report from an attached
+ * device.
+ * item Pointer to the report item of interest in a struct hid_rptinfo_s
+ * item array.
+ *
+ * Returned Value:
+ * Zero on success, otherwise a negated errno value.
+ *
+ ****************************************************************************/
+
+int hid_getitem(FAR const uint8_t *report, FAR struct hid_rptitem_s *item)
+{
+ uint16_t remaining = item->attrib.bitsize;
+ uint16_t offset = item->bitoffset;
+ uint32_t mask = (1 << 0);
+
+ if (item->id)
+ {
+ if (item->id != report[0])
+ {
+ return -ENOENT;
+ }
+
+ report++;
+ }
+
+ item->previous = item->value;
+ item->value = 0;
+
+ while (remaining--)
+ {
+ if (report[offset >> 3] & (1 << (offset & 7)))
+ {
+ item->value |= mask;
+ }
+
+ offset++;
+ mask <<= 1;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: hid_putitem
+ *
+ * Desription:
+ * Retrieves the given report item's value out of the Value member of the
+ * report item's struct hid_rptitem_s structure and places it into the correct
+ * position in the HID report buffer. The report buffer is assumed to have
+ * the appropriate bits cleared before calling this function (i.e., the
+ * buffer should be explicitly cleared before report values are added).
+ *
+ * When called, this copies the report item's Value element to it's
+ * previous element for easy checking to see if an item's value has
+ * changed before sending a report.
+ *
+ * If the device has multiple HID reports, the first byte in the report is
+ * set to the report ID of the given item.
+ *
+ * Input Parameters:
+ * report Buffer holding the current OUT or FEATURE report data.
+ * item Pointer to the report item of interest in a struct hid_rptinfo_s
+ * item array.
+ *
+ ****************************************************************************/
+
+#if 0 /* Not needed by host */
+void hid_putitem(FAR uint8_t *report, struct hid_rptitem_s *item)
+{
+ uint16_t remaining = item->attrib.bitsize;
+ uint16_t offset = item->bitoffset;
+ uint32_t mask = (1 << 0);
+
+ if (item->id)
+ {
+ report[0] = item->id;
+ report++;
+ }
+
+ item->previous = item->value;
+
+ while (remaining--)
+ {
+ if (item->value & (1 << (offset & 7)))
+ {
+ report[offset >> 3] |= mask;
+ }
+
+ offset++;
+ mask <<= 1;
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: hid_reportsize
+ *
+ * Description:
+ * Retrieves the size of a given HID report in bytes from it's Report ID.
+ *
+ * InputParameters:
+ * rptinfo Pointer to a struct hid_rptinfo_s instance containing the parser output.
+ * id Report ID of the report whose size is to be retrieved.
+ * rpttype Type of the report whose size is to be determined, a valued from the
+ * HID_ReportItemTypes_t enum.
+ *
+ * Size of the report in bytes, or 0 if the report does not exist.
+ *
+ ****************************************************************************/
+
+size_t hid_reportsize(FAR struct hid_rptinfo_s *rptinfo, uint8_t id, uint8_t rpttype)
+{
+ int i;
+ for (i = 0; i < CONFIG_HID_MAXIDS; i++)
+ {
+ size_t size = rptinfo->rptsize[i].size[rpttype];
+
+ if (rptinfo->rptsize[i].id == id)
+ {
+ return ((size >> 3) + ((size & 0x07) ? 1 : 0));
+ }
+ }
+
+ return 0;
+}
diff --git a/nuttx/drivers/usbhost/usbhost_enumerate.c b/nuttx/drivers/usbhost/usbhost_enumerate.c
new file mode 100644
index 000000000..26b93bf36
--- /dev/null
+++ b/nuttx/drivers/usbhost/usbhost_enumerate.c
@@ -0,0 +1,518 @@
+/*******************************************************************************
+ * drivers/usbhost/usbhost_enumerate.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Authors: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Included Files
+ *******************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbhost.h>
+
+/*******************************************************************************
+ * Definitions
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Private Types
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Private Function Prototypes
+ *******************************************************************************/
+
+static inline uint16_t usbhost_getle16(const uint8_t *val);
+static void usbhost_putle16(uint8_t *dest, uint16_t val);
+
+static inline int usbhost_devdesc(const struct usb_devdesc_s *devdesc,
+ struct usbhost_id_s *id);
+static inline int usbhost_configdesc(const uint8_t *configdesc, int desclen,
+ struct usbhost_id_s *id);
+static inline int usbhost_classbind(FAR struct usbhost_driver_s *drvr,
+ const uint8_t *configdesc, int desclen,
+ struct usbhost_id_s *id, uint8_t funcaddr,
+ FAR struct usbhost_class_s **class);
+
+/*******************************************************************************
+ * Private Data
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Public Data
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Private Functions
+ *******************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_getle16
+ *
+ * Description:
+ * Get a (possibly unaligned) 16-bit little endian value.
+ *
+ *******************************************************************************/
+
+static inline uint16_t usbhost_getle16(const uint8_t *val)
+{
+ return (uint16_t)val[1] << 8 | (uint16_t)val[0];
+}
+
+/****************************************************************************
+ * Name: usbhost_putle16
+ *
+ * Description:
+ * Put a (possibly unaligned) 16-bit little endian value.
+ *
+ *******************************************************************************/
+
+static void usbhost_putle16(uint8_t *dest, uint16_t val)
+{
+ dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
+ dest[1] = val >> 8;
+}
+
+/*******************************************************************************
+ * Name: usbhost_devdesc
+ *
+ * Description:
+ * A configuration descriptor has been obtained from the device. Find the
+ * ID information for the class that supports this device.
+ *
+ *******************************************************************************/
+
+static inline int usbhost_devdesc(FAR const struct usb_devdesc_s *devdesc,
+ FAR struct usbhost_id_s *id)
+{
+ /* Clear the ID info */
+
+ memset(id, 0, sizeof(struct usbhost_id_s));
+
+ /* Pick off the class ID info */
+
+ id->base = devdesc->classid;
+ id->subclass = devdesc->subclass;
+ id->proto = devdesc->protocol;
+
+ /* Pick off the VID and PID as well (for vendor specfic devices) */
+
+ id->vid = usbhost_getle16(devdesc->vendor);
+ id->pid = usbhost_getle16(devdesc->product);
+
+ uvdbg("class:%d subclass:%04x protocol:%04x vid:%d pid:%d\n",
+ id->base, id->subclass, id->proto, id->vid, id->pid);
+ return OK;
+}
+
+/*******************************************************************************
+ * Name: usbhost_configdesc
+ *
+ * Description:
+ * A configuration descriptor has been obtained from the device. Find the
+ * ID information for the class that supports this device.
+ *
+ *******************************************************************************/
+
+static inline int usbhost_configdesc(const uint8_t *configdesc, int cfglen,
+ struct usbhost_id_s *id)
+{
+ struct usb_cfgdesc_s *cfgdesc;
+ struct usb_ifdesc_s *ifdesc;
+ int remaining;
+
+ DEBUGASSERT(configdesc != NULL && cfglen >= USB_SIZEOF_CFGDESC);
+
+ /* Verify that we were passed a configuration descriptor */
+
+ cfgdesc = (struct usb_cfgdesc_s *)configdesc;
+ uvdbg("cfg len:%d total len:%d\n", cfgdesc->len, cfglen);
+
+ if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
+ {
+ return -EINVAL;
+ }
+
+ /* Skip to the next entry descriptor */
+
+ configdesc += cfgdesc->len;
+ remaining = cfglen - cfgdesc->len;
+
+ /* Loop where there are more dscriptors to examine */
+
+ memset(id, 0, sizeof(FAR struct usb_desc_s));
+ while (remaining >= sizeof(struct usb_desc_s))
+ {
+ /* What is the next descriptor? Is it an interface descriptor? */
+
+ ifdesc = (struct usb_ifdesc_s *)configdesc;
+ if (ifdesc->type == USB_DESC_TYPE_INTERFACE)
+ {
+ /* Yes, extract the class information from the interface descriptor.
+ * Typically these values are zero meaning that the "real" ID
+ * information resides in the device descriptor.
+ */
+
+ DEBUGASSERT(remaining >= sizeof(struct usb_ifdesc_s));
+ id->base = ifdesc->classid;
+ id->subclass = ifdesc->subclass;
+ id->proto = ifdesc->protocol;
+ uvdbg("class:%d subclass:%d protocol:%d\n",
+ id->base, id->subclass, id->proto);
+ return OK;
+ }
+
+ /* Increment the address of the next descriptor */
+
+ configdesc += ifdesc->len;
+ remaining -= ifdesc->len;
+ }
+
+ return -ENOENT;
+}
+
+/*******************************************************************************
+ * Name: usbhost_classbind
+ *
+ * Description:
+ * A configuration descriptor has been obtained from the device. Try to
+ * bind this configuration descriptor with a supported class.
+ *
+ *******************************************************************************/
+
+static inline int usbhost_classbind(FAR struct usbhost_driver_s *drvr,
+ const uint8_t *configdesc, int desclen,
+ struct usbhost_id_s *id, uint8_t funcaddr,
+ FAR struct usbhost_class_s **class)
+{
+ FAR struct usbhost_class_s *devclass;
+ const struct usbhost_registry_s *reg;
+ int ret = -EINVAL;
+
+ /* Is there is a class implementation registered to support this device. */
+
+ reg = usbhost_findclass(id);
+ uvdbg("usbhost_findclass: %p\n", reg);
+ if (reg)
+ {
+ /* Yes.. there is a class for this device. Get an instance of
+ * its interface.
+ */
+
+ ret = -ENOMEM;
+ devclass = CLASS_CREATE(reg, drvr, id);
+ uvdbg("CLASS_CREATE: %p\n", devclass);
+ if (devclass)
+ {
+ /* Then bind the newly instantiated class instance */
+
+ ret = CLASS_CONNECT(devclass, configdesc, desclen, funcaddr);
+ if (ret != OK)
+ {
+ /* On failures, call the class disconnect method which
+ * should then free the allocated devclass instance.
+ */
+
+ udbg("CLASS_CONNECT failed: %d\n", ret);
+ CLASS_DISCONNECTED(devclass);
+ }
+ else
+ {
+ *class = devclass;
+ }
+ }
+ }
+
+ uvdbg("Returning: %d\n", ret);
+ return ret;
+}
+
+/*******************************************************************************
+ * Public Functions
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Name: usbhost_enumerate
+ *
+ * Description:
+ * Enumerate the connected device. As part of this enumeration process,
+ * the driver will (1) get the device's configuration descriptor, (2)
+ * extract the class ID info from the configuration descriptor, (3) call
+ * usbhost_findclass() to find the class that supports this device, (4)
+ * call the create() method on the struct usbhost_registry_s interface
+ * to get a class instance, and finally (5) call the configdesc() method
+ * of the struct usbhost_class_s interface. After that, the class is in
+ * charge of the sequence of operations.
+ *
+ * Input Parameters:
+ * drvr - The USB host driver instance obtained as a parameter from the call to
+ * the class create() method.
+ * funcaddr - The USB address of the function containing the endpoint that EP0
+ * controls
+ * class - If the class driver for the device is successful located
+ * and bound to the driver, the allocated class instance is returned into
+ * this caller-provided memory location.
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value is
+ * returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * - Only a single class bound to a single device is supported.
+ * - Called from a single thread so no mutual exclusion is required.
+ * - Never called from an interrupt handler.
+ *
+ *******************************************************************************/
+
+int usbhost_enumerate(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr,
+ FAR struct usbhost_class_s **class)
+{
+ struct usb_ctrlreq_s *ctrlreq;
+ struct usbhost_id_s id;
+ size_t maxlen;
+ unsigned int cfglen;
+ uint8_t maxpacketsize;
+ uint8_t *buffer;
+ int ret;
+
+ DEBUGASSERT(drvr && class);
+
+ /* Allocate descriptor buffers for use in this function. We will need two:
+ * One for the request and one for the data buffer.
+ */
+
+ ret = DRVR_ALLOC(drvr, (FAR uint8_t **)&ctrlreq, &maxlen);
+ if (ret != OK)
+ {
+ udbg("DRVR_ALLOC failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = DRVR_ALLOC(drvr, &buffer, &maxlen);
+ if (ret != OK)
+ {
+ udbg("DRVR_ALLOC failed: %d\n", ret);
+ goto errout;
+ }
+
+ /* Set max pkt size = 8 */
+
+ DRVR_EP0CONFIGURE(drvr, 0, 8);
+
+ /* Read first 8 bytes of the device descriptor */
+
+ ctrlreq->type = USB_REQ_DIR_IN|USB_REQ_RECIPIENT_DEVICE;
+ ctrlreq->req = USB_REQ_GETDESCRIPTOR;
+ usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_DEVICE << 8));
+ usbhost_putle16(ctrlreq->index, 0);
+ usbhost_putle16(ctrlreq->len, 8);
+
+ ret = DRVR_CTRLIN(drvr, ctrlreq, buffer);
+ if (ret != OK)
+ {
+ udbg("ERROR: GETDESCRIPTOR/DEVICE, DRVR_CTRLIN returned %d\n", ret);
+ goto errout;
+ }
+
+ /* Extract the correct max packetsize from the device descriptor */
+
+ maxpacketsize = ((struct usb_devdesc_s *)buffer)->mxpacketsize;
+ uvdbg("maxpacksetsize: %d\n", maxpacketsize);
+
+ /* And reconfigure EP0 */
+
+ DRVR_EP0CONFIGURE(drvr, 0, maxpacketsize);
+
+ /* Now read the full device descriptor */
+
+ ctrlreq->type = USB_REQ_DIR_IN|USB_REQ_RECIPIENT_DEVICE;
+ ctrlreq->req = USB_REQ_GETDESCRIPTOR;
+ usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_DEVICE << 8));
+ usbhost_putle16(ctrlreq->index, 0);
+ usbhost_putle16(ctrlreq->len, USB_SIZEOF_DEVDESC);
+
+ ret = DRVR_CTRLIN(drvr, ctrlreq, buffer);
+ if (ret != OK)
+ {
+ udbg("ERROR: GETDESCRIPTOR/DEVICE, DRVR_CTRLIN returned %d\n", ret);
+ goto errout;
+ }
+
+ /* Get class identification information from the device descriptor. Most
+ * devices set this to USB_CLASS_PER_INTERFACE (zero) and provide the
+ * identification informatino in the interface descriptor(s). That allows
+ * a device to support multiple, different classes.
+ */
+
+ (void)usbhost_devdesc((struct usb_devdesc_s *)buffer, &id);
+
+ /* Set the USB device address to the value in the 'funcaddr' input */
+
+ ctrlreq->type = USB_REQ_DIR_OUT|USB_REQ_RECIPIENT_DEVICE;
+ ctrlreq->req = USB_REQ_SETADDRESS;
+ usbhost_putle16(ctrlreq->value, (uint16_t)funcaddr);
+ usbhost_putle16(ctrlreq->index, 0);
+ usbhost_putle16(ctrlreq->len, 0);
+
+ ret = DRVR_CTRLOUT(drvr, ctrlreq, NULL);
+ if (ret != OK)
+ {
+ udbg("ERROR: SETADDRESS DRVR_CTRLOUT returned %d\n", ret);
+ goto errout;
+ }
+ usleep(2*1000);
+
+ /* Modify control pipe with the provided USB device address */
+
+ DRVR_EP0CONFIGURE(drvr, funcaddr, maxpacketsize);
+
+ /* Get the configuration descriptor (only), index == 0. Should not be
+ * hard-coded! More logic is needed in order to handle devices with
+ * multiple configurations.
+ */
+
+ ctrlreq->type = USB_REQ_DIR_IN|USB_REQ_RECIPIENT_DEVICE;
+ ctrlreq->req = USB_REQ_GETDESCRIPTOR;
+ usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_CONFIG << 8));
+ usbhost_putle16(ctrlreq->index, 0);
+ usbhost_putle16(ctrlreq->len, USB_SIZEOF_CFGDESC);
+
+ ret = DRVR_CTRLIN(drvr, ctrlreq, buffer);
+ if (ret != OK)
+ {
+ udbg("ERROR: GETDESCRIPTOR/CONFIG, DRVR_CTRLIN returned %d\n", ret);
+ goto errout;
+ }
+
+ /* Extract the full size of the configuration data */
+
+ cfglen = (unsigned int)usbhost_getle16(((struct usb_cfgdesc_s *)buffer)->totallen);
+ uvdbg("sizeof config data: %d\n", cfglen);
+
+ /* Get all of the configuration descriptor data, index == 0 (Should not be
+ * hard-coded!)
+ */
+
+ ctrlreq->type = USB_REQ_DIR_IN|USB_REQ_RECIPIENT_DEVICE;
+ ctrlreq->req = USB_REQ_GETDESCRIPTOR;
+ usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_CONFIG << 8));
+ usbhost_putle16(ctrlreq->index, 0);
+ usbhost_putle16(ctrlreq->len, cfglen);
+
+ ret = DRVR_CTRLIN(drvr, ctrlreq, buffer);
+ if (ret != OK)
+ {
+ udbg("ERROR: GETDESCRIPTOR/CONFIG, DRVR_CTRLIN returned %d\n", ret);
+ goto errout;
+ }
+
+ /* Select device configuration 1 (Should not be hard-coded!) */
+
+ ctrlreq->type = USB_REQ_DIR_OUT|USB_REQ_RECIPIENT_DEVICE;
+ ctrlreq->req = USB_REQ_SETCONFIGURATION;
+ usbhost_putle16(ctrlreq->value, 1);
+ usbhost_putle16(ctrlreq->index, 0);
+ usbhost_putle16(ctrlreq->len, 0);
+
+ ret = DRVR_CTRLOUT(drvr, ctrlreq, NULL);
+ if (ret != OK)
+ {
+ udbg("ERROR: SETCONFIGURATION, DRVR_CTRLOUT returned %d\n", ret);
+ goto errout;
+ }
+
+ /* Free the descriptor buffer that we were using for the request buffer.
+ * It is not needed further here but it may be needed by the class driver
+ * during its connection operations.
+ */
+
+ DRVR_FREE(drvr, (uint8_t*)ctrlreq);
+ ctrlreq = NULL;
+
+ /* Was the class identification information provided in the device descriptor?
+ * Or do we need to find it in the interface descriptor(s)?
+ */
+
+ if (id.base == USB_CLASS_PER_INTERFACE)
+ {
+ /* Get the class identification information for this device from the
+ * interface descriptor(s). Hmmm.. More logic is need to handle the
+ * case of multiple interface descriptors.
+ */
+
+ ret = usbhost_configdesc(buffer, cfglen, &id);
+ if (ret != OK)
+ {
+ udbg("ERROR: usbhost_configdesc returned %d\n", ret);
+ goto errout;
+ }
+ }
+
+ /* Some devices may require some delay before initialization */
+
+ usleep(100*1000);
+
+ /* Parse the configuration descriptor and bind to the class instance for the
+ * device. This needs to be the last thing done because the class driver
+ * will begin configuring the device.
+ */
+
+ ret = usbhost_classbind(drvr, buffer, cfglen, &id, funcaddr, class);
+ if (ret != OK)
+ {
+ udbg("ERROR: usbhost_classbind returned %d\n", ret);
+ }
+
+errout:
+ if (buffer)
+ {
+ DRVR_FREE(drvr, buffer);
+ }
+
+ if (ctrlreq)
+ {
+ DRVR_FREE(drvr, (uint8_t*)ctrlreq);
+ }
+ return ret;
+}
diff --git a/nuttx/drivers/usbhost/usbhost_findclass.c b/nuttx/drivers/usbhost/usbhost_findclass.c
new file mode 100644
index 000000000..3e38670cf
--- /dev/null
+++ b/nuttx/drivers/usbhost/usbhost_findclass.c
@@ -0,0 +1,199 @@
+/****************************************************************************
+ * drivers/usbhost/usbhost_findclass.c
+ *
+ * Copyright (C) 2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <debug.h>
+
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbhost.h>
+#include <arch/irq.h>
+
+#include "usbhost_registry.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_idmatch
+ *
+ * Description:
+ * Check if the class ID matches what the host controller found.
+ *
+ * Input Parameters:
+ * classid - ID info for the class under consideration.
+ * devid - ID info reported by the device.
+ *
+ * Returned Values:
+ * TRUE - the class will support this device.
+ *
+ ****************************************************************************/
+
+static bool usbhost_idmatch(const struct usbhost_id_s *classid,
+ const struct usbhost_id_s *devid)
+{
+ uvdbg("Compare to class:%d subclass:%d protocol:%d vid:%04x pid:%04x\n",
+ classid->base, classid->subclass, classid->proto,
+ classid->vid, classid->pid);
+
+ /* The base class ID, subclass and protocol have to match up in any event */
+
+ if (devid->base == classid->base &&
+ devid->subclass == classid->subclass &&
+ devid->proto == classid->proto)
+ {
+ /* If this is a vendor-specific class ID, then the VID and PID have to
+ * match as well.
+ */
+
+ if (devid->base == USB_CLASS_VENDOR_SPEC)
+ {
+ /* Vendor specific... do the VID and PID also match? */
+
+ if (devid->vid == classid->vid && devid->pid == classid->pid)
+ {
+ /* Yes.. then we have a match */
+
+ return true;
+ }
+ }
+ else
+ {
+ /* Not vendor specific? Then we have a match */
+
+ return true;
+ }
+ }
+
+ /* No match.. not supported */
+
+ return false;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_findclass
+ *
+ * Description:
+ * Find a USB host class implementation previously registered by
+ * usbhost_registerclass(). On success, an instance of struct
+ * usbhost_registry_s will be returned. That instance will contain all of
+ * the information that will be needed to obtain and bind a struct
+ * usbhost_class_s instance for the device.
+ *
+ * Input Parameters:
+ * id - Identifies the USB device class that has connect to the USB host.
+ *
+ * Returned Values:
+ * On success this function will return a non-NULL instance of struct
+ * usbhost_registry_s. NULL will be returned on failure. This function
+ * can only fail if (1) id is NULL, or (2) no USB host class is registered
+ * that matches the device class ID.
+ *
+ ****************************************************************************/
+
+const struct usbhost_registry_s *usbhost_findclass(const struct usbhost_id_s *id)
+{
+ struct usbhost_registry_s *class;
+ irqstate_t flags;
+ int ndx;
+
+ DEBUGASSERT(id);
+ uvdbg("Looking for class:%d subclass:%d protocol:%d vid:%04x pid:%04x\n",
+ id->base, id->subclass, id->proto, id->vid, id->pid);
+
+ /* g_classregistry is a singly-linkedlist of class ID information added by
+ * calls to usbhost_registerclass(). Since this list is accessed from USB
+ * host controller interrupt handling logic, accesses to this list must be
+ * protected by disabling interrupts.
+ */
+
+ flags = irqsave();
+
+ /* Examine each register class in the linked list */
+
+ for (class = g_classregistry; class; class = class->flink)
+ {
+ /* If the registered class supports more than one ID, subclass, or
+ * protocol, then try each.
+ */
+
+ uvdbg("Checking class:%p nids:%d\n", class, class->nids);
+ for (ndx = 0; ndx < class->nids; ndx++)
+ {
+ /* Did we find a matching ID? */
+
+ if (usbhost_idmatch(&class->id[ndx], id))
+ {
+ /* Yes.. restore interrupts and return the class info */
+
+ irqrestore(flags);
+ return class;
+ }
+ }
+ }
+
+ /* Not found... restore interrupts and return NULL */
+
+ irqrestore(flags);
+ return NULL;
+}
+
diff --git a/nuttx/drivers/usbhost/usbhost_hidkbd.c b/nuttx/drivers/usbhost/usbhost_hidkbd.c
new file mode 100644
index 000000000..e69d68e7b
--- /dev/null
+++ b/nuttx/drivers/usbhost/usbhost_hidkbd.c
@@ -0,0 +1,2052 @@
+/****************************************************************************
+ * drivers/usbhost/usbhost_hidkbd.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <poll.h>
+#include <semaphore.h>
+#include <time.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbhost.h>
+#include <nuttx/usb/hid.h>
+
+/* Don't compile if prerequisites are not met */
+
+#if defined(CONFIG_USBHOST) && !defined(CONFIG_USBHOST_INT_DISABLE) && CONFIG_NFILE_DESCRIPTORS > 0
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+/* This determines how often the USB keyboard will be polled in units of
+ * of microseconds. The default is 100MS.
+ */
+
+#ifndef CONFIG_HIDKBD_POLLUSEC
+# define CONFIG_HIDKBD_POLLUSEC (100*1000)
+#endif
+
+/* Worker thread is needed, unfortunately, to handle some cornercase failure
+ * conditions. This is kind of wasteful and begs for a re-design.
+ */
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+# warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
+#endif
+
+/* Signals must not be disabled as they are needed by usleep. Need to have
+ * CONFIG_DISABLE_SIGNALS=n
+ */
+
+#ifdef CONFIG_DISABLE_SIGNALS
+# warning "Signal support is required (CONFIG_DISABLE_SIGNALS)"
+#endif
+
+/* Provide some default values for other configuration settings */
+
+#ifndef CONFIG_HIDKBD_DEFPRIO
+# define CONFIG_HIDKBD_DEFPRIO 50
+#endif
+
+#ifndef CONFIG_HIDKBD_STACKSIZE
+# define CONFIG_HIDKBD_STACKSIZE 1024
+#endif
+
+#ifndef CONFIG_HIDKBD_BUFSIZE
+# define CONFIG_HIDKBD_BUFSIZE 64
+#endif
+
+#ifndef CONFIG_HIDKBD_NPOLLWAITERS
+# define CONFIG_HIDKBD_NPOLLWAITERS 2
+#endif
+
+/* The default is to support scancode mapping for the standard 104 key
+ * keyboard. Setting CONFIG_HIDKBD_RAWSCANCODES will disable all scancode
+ * mapping; Setting CONFIG_HIDKBD_ALLSCANCODES will enable mapping of all
+ * scancodes;
+ */
+
+#ifndef CONFIG_HIDKBD_RAWSCANCODES
+# ifdef CONFIG_HIDKBD_ALLSCANCODES
+# define USBHID_NUMSCANCODES (USBHID_KBDUSE_MAX+1)
+# else
+# define USBHID_NUMSCANCODES 104
+# endif
+#endif
+
+/* Driver support ***********************************************************/
+/* This format is used to construct the /dev/kbd[n] device driver path. It
+ * defined here so that it will be used consistently in all places.
+ */
+
+#define DEV_FORMAT "/dev/kbd%c"
+#define DEV_NAMELEN 11
+
+/* Used in usbhost_cfgdesc() */
+
+#define USBHOST_IFFOUND 0x01 /* Required I/F descriptor found */
+#define USBHOST_EPINFOUND 0x02 /* Required interrupt IN EP descriptor found */
+#define USBHOST_EPOUTFOUND 0x04 /* Optional interrupt OUT EP descriptor found */
+#define USBHOST_RQDFOUND (USBHOST_IFFOUND|USBHOST_EPINFOUND)
+#define USBHOST_ALLFOUND (USBHOST_RQDFOUND|USBHOST_EPOUTFOUND)
+
+#define USBHOST_MAX_CREFS 0x7fff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure contains the internal, private state of the USB host
+ * keyboard storage class.
+ */
+
+struct usbhost_state_s
+{
+ /* This is the externally visible portion of the state */
+
+ struct usbhost_class_s class;
+
+ /* This is an instance of the USB host driver bound to this class instance */
+
+ struct usbhost_driver_s *drvr;
+
+ /* The remainder of the fields are provide o the keyboard class driver */
+
+ char devchar; /* Character identifying the /dev/kbd[n] device */
+ volatile bool disconnected; /* TRUE: Device has been disconnected */
+ volatile bool polling; /* TRUE: Poll thread is running */
+ volatile bool open; /* TRUE: The keyboard device is open */
+ volatile bool waiting; /* TRUE: waiting for keyboard data */
+ uint8_t ifno; /* Interface number */
+ int16_t crefs; /* Reference count on the driver instance */
+ sem_t exclsem; /* Used to maintain mutual exclusive access */
+ sem_t waitsem; /* Used to wait for keyboard data */
+ FAR uint8_t *tbuffer; /* The allocated transfer buffer */
+ size_t tbuflen; /* Size of the allocated transfer buffer */
+ pid_t pollpid; /* PID of the poll task */
+ struct work_s work; /* For cornercase error handling by the worker thread */
+
+ /* Endpoints:
+ * EP0 (Control):
+ * - Receiving and responding to requests for USB control and class data.
+ * - IN data when polled by the HID class driver (Get_Report)
+ * - OUT data from the host.
+ * EP Interrupt IN:
+ * - Receiving asynchronous (unrequested) IN data from the device.
+ * EP Interrrupt OUT (optional):
+ * - Transmitting low latency OUT data to the device.
+ * - If not present, EP0 used.
+ */
+
+ usbhost_ep_t epin; /* Interrupt IN endpoint */
+ usbhost_ep_t epout; /* Optional interrupt OUT endpoint */
+
+ /* The following is a list if poll structures of threads waiting for
+ * driver events. The 'struct pollfd' reference for each open is also
+ * retained in the f_priv field of the 'struct file'.
+ */
+
+#ifndef CONFIG_DISABLE_POLL
+ struct pollfd *fds[CONFIG_HIDKBD_NPOLLWAITERS];
+#endif
+
+ /* Buffer used to collect and buffer incoming keyboard characters */
+
+ volatile uint16_t headndx; /* Buffer head index */
+ volatile uint16_t tailndx; /* Buffer tail index */
+ uint8_t kbdbuffer[CONFIG_HIDKBD_BUFSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Semaphores */
+
+static void usbhost_takesem(sem_t *sem);
+#define usbhost_givesem(s) sem_post(s);
+
+/* Polling support */
+
+#ifndef CONFIG_DISABLE_POLL
+static void usbhost_pollnotify(FAR struct usbhost_state_s *dev);
+#else
+# define usbhost_pollnotify(dev)
+#endif
+
+/* Memory allocation services */
+
+static inline FAR struct usbhost_state_s *usbhost_allocclass(void);
+static inline void usbhost_freeclass(FAR struct usbhost_state_s *class);
+
+/* Device name management */
+
+static int usbhost_allocdevno(FAR struct usbhost_state_s *priv);
+static void usbhost_freedevno(FAR struct usbhost_state_s *priv);
+static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, char *devname);
+
+/* Keyboard polling thread */
+
+static void usbhost_destroy(FAR void *arg);
+static inline uint8_t usbhost_mapscancode(uint8_t scancode, uint8_t modifier);
+static int usbhost_kbdpoll(int argc, char *argv[]);
+
+/* Helpers for usbhost_connect() */
+
+static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr);
+static inline int usbhost_devinit(FAR struct usbhost_state_s *priv);
+
+/* (Little Endian) Data helpers */
+
+static inline uint16_t usbhost_getle16(const uint8_t *val);
+static inline void usbhost_putle16(uint8_t *dest, uint16_t val);
+static inline uint32_t usbhost_getle32(const uint8_t *val);
+#if 0 /* Not used */
+static void usbhost_putle32(uint8_t *dest, uint32_t val);
+#endif
+
+/* Transfer descriptor memory management */
+
+static inline int usbhost_tdalloc(FAR struct usbhost_state_s *priv);
+static inline int usbhost_tdfree(FAR struct usbhost_state_s *priv);
+
+/* struct usbhost_registry_s methods */
+
+static struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *drvr,
+ FAR const struct usbhost_id_s *id);
+
+/* struct usbhost_class_s methods */
+
+static int usbhost_connect(FAR struct usbhost_class_s *class,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr);
+static int usbhost_disconnected(FAR struct usbhost_class_s *class);
+
+/* Driver methods. We export the keyboard as a standard character driver */
+
+static int usbhost_open(FAR struct file *filep);
+static int usbhost_close(FAR struct file *filep);
+static ssize_t usbhost_read(FAR struct file *filep,
+ FAR char *buffer, size_t len);
+static ssize_t usbhost_write(FAR struct file *filep,
+ FAR const char *buffer, size_t len);
+#ifndef CONFIG_DISABLE_POLL
+static int usbhost_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This structure provides the registry entry ID informatino that will be
+ * used to associate the USB host keyboard class driver to a connected USB
+ * device.
+ */
+
+static const const struct usbhost_id_s g_id =
+{
+ USB_CLASS_HID, /* base */
+ USBHID_SUBCLASS_BOOTIF, /* subclass */
+ USBHID_PROTOCOL_KEYBOARD, /* proto */
+ 0, /* vid */
+ 0 /* pid */
+};
+
+/* This is the USB host storage class's registry entry */
+
+static struct usbhost_registry_s g_skeleton =
+{
+ NULL, /* flink */
+ usbhost_create, /* create */
+ 1, /* nids */
+ &g_id /* id[] */
+};
+
+static const struct file_operations usbhost_fops =
+{
+ usbhost_open, /* open */
+ usbhost_close, /* close */
+ usbhost_read, /* read */
+ usbhost_write, /* write */
+ 0, /* seek */
+ 0 /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , usbhost_poll /* poll */
+#endif
+};
+
+/* This is a bitmap that is used to allocate device names /dev/kbda-z. */
+
+static uint32_t g_devinuse;
+
+/* The following are used to managed the class creation operation */
+
+static sem_t g_exclsem; /* For mutually exclusive thread creation */
+static sem_t g_syncsem; /* Thread data passing interlock */
+static struct usbhost_state_s *g_priv; /* Data passed to thread */
+
+/* The following tables map keyboard scan codes to printable ASIC
+ * characters. There is no support here for function keys or cursor
+ * controls.
+ */
+
+#ifndef CONFIG_HIDKBD_RAWSCANCODES
+static const uint8_t ucmap[USBHID_NUMSCANCODES] =
+{
+ 0, 0, 0, 0, 'A', 'B', 'C', 'D', /* 0x00-0x07: Reserved, errors, A-D */
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', /* 0x08-0x0f: E-L */
+ 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 0x10-0x17: M-T */
+ 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', /* 0x18-0x1f: U-Z,!,@ */
+ '#', '$', '%', '^', '&', '*', '(', ')', /* 0x20-0x27: #,$,%,^,&,*,(,) */
+ '\n', '\033', '\177', 0, ' ', '_', '+', '{', /* 0x28-0x2f: Enter,escape,del,back-tab,space,_,+,{ */
+ '}', '|', 0, ':', '"', 0, '<', '>', /* 0x30-0x37: },|,Non-US tilde,:,",grave tidle,<,> */
+ '?', 0, 0, 0, 0, 0, 0, 0, /* 0x38-0x3f: /,CapsLock,F1,F2,F3,F4,F5,F6 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40-0x47: F7,F8,F9,F10,F11,F12,PrtScn,sScrollLock */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48-0x4f: Pause,Insert,Home,PageUp,DeleteForward,End,PageDown,RightArrow */
+ 0, 0, 0, 0, '/', '*', '-', '+', /* 0x50-0x57: LeftArrow,DownArrow,UpArrow,Num Lock,/,*,-,+ */
+ '\n', '1', '2', '3', '4', '4', '6', '7', /* 0x58-0x5f: Enter,1-7 */
+ '8', '9', '0', '.', 0, 0, 0, '=', /* 0x60-0x67: 8-9,0,.,Non-US \,Application,Power,= */
+#ifdef CONFIG_HIDKBD_ALLSCANCODES
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68-0x6f: F13,F14,F15,F16,F17,F18,F19,F20 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77: F21,F22,F23,F24,Execute,Help,Menu,Select */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x78-0x7f: Stop,Again,Undo,Cut,Copy,Paste,Find,Mute */
+ 0, 0, 0, 0, 0, ',', 0, 0, /* 0x80-0x87: VolUp,VolDown,LCapsLock,lNumLock,LScrollLock,,,=,International1 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x88-0x8f: International 2-9 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x97: LAN 1-8 */
+ 0, 0, 0, 0, 0, 0, '\n', 0, /* 0x98-0x9f: LAN 9,Ease,SysReq,Cancel,Clear,Prior,Return,Separator */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xa7: Out,Oper,Clear,CrSel,Excel,(reserved) */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa8-0xaf: (reserved) */
+ 0, 0, 0, 0, 0, 0, '(', ')', /* 0xb0-0xb7: 00,000,ThouSeparator,DecSeparator,CurrencyUnit,SubUnit,(,) */
+ '{', '}', '\t', \177, 'A', 'B', 'C', 'D', /* 0xb8-0xbf: {,},tab,backspace,A-D */
+ 'F', 'F', 0, '^', '%', '<', '>', '&', /* 0xc0-0xc7: E-F,XOR,^,%,<,>,& */
+ 0, '|', 0, ':', '%', ' ', '@', '!', /* 0xc8-0xcf: &&,|,||,:,#, ,@,! */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0-0xd7: Memory Store,Recall,Clear,Add,Subtract,Muliply,Divide,+/- */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd8-0xdf: Clear,ClearEntry,Binary,Octal,Decimal,Hexadecimal */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0-0xe7: Left Ctrl,Shift,Alt,GUI, Right Ctrl,Shift,Alt,GUI */
+#endif
+};
+
+static const uint8_t lcmap[USBHID_NUMSCANCODES] =
+{
+ 0, 0, 0, 0, 'a', 'b', 'c', 'd', /* 0x00-0x07: Reserved, errors, a-d */
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', /* 0x08-0x0f: e-l */
+ 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', /* 0x10-0x17: m-t */
+ 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', /* 0x18-0x1f: u-z,1-2 */
+ '3', '4', '5', '6', '7', '8', '9', '0', /* 0x20-0x27: 3-9,0 */
+ '\n', '\033', '\177', '\t', ' ', '-', '=', '[', /* 0x28-0x2f: Enter,escape,del,tab,space,-,=,[ */
+ ']', '\\', '\234', ';', '\'', 0, ',', '.', /* 0x30-0x37: ],\,Non-US pound,;,',grave accent,,,. */
+ '/', 0, 0, 0, 0, 0, 0, 0, /* 0x38-0x3f: /,CapsLock,F1,F2,F3,F4,F5,F6 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40-0x47: F7,F8,F9,F10,F11,F12,PrtScn,ScrollLock */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48-0x4f: Pause,Insert,Home,PageUp,DeleteForward,End,PageDown,RightArrow */
+ 0, 0, 0, 0, '/', '*', '-', '+', /* 0x50-0x57: LeftArrow,DownArrow,UpArrow,Num Lock,/,*,-,+ */
+ '\n', '1', '2', '3', '4', '4', '6', '7', /* 0x58-0x5f: Enter,1-7 */
+ '8', '9', '0', '.', 0, 0, 0, '=', /* 0x60-0x67: 8-9,0,.,Non-US \,Application,Power,= */
+#ifdef CONFIG_HIDKBD_ALLSCANCODES
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68-0x6f: F13,F14,F15,F16,F17,F18,F19,F20 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77: F21,F22,F23,F24,Execute,Help,Menu,Select */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x78-0x7f: Stop,Again,Undo,Cut,Copy,Paste,Find,Mute */
+ 0, 0, 0, 0, 0, ',', 0, 0, /* 0x80-0x87: VolUp,VolDown,LCapsLock,lNumLock,LScrollLock,,,=,International1 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x88-0x8f: International 2-9 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x97: LAN 1-8 */
+ 0, 0, 0, 0, 0, 0, '\n', 0, /* 0x98-0x9f: LAN 9,Ease,SysReq,Cancel,Clear,Prior,Return,Separator */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xa7: Out,Oper,Clear,CrSel,Excel,(reserved) */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa8-0xaf: (reserved) */
+ 0, 0, 0, 0, 0, 0, '(', ')', /* 0xb0-0xb7: 00,000,ThouSeparator,DecSeparator,CurrencyUnit,SubUnit,(,) */
+ '{', '}', '\t', '\177', 'A', 'B', 'C', 'D', /* 0xb8-0xbf: {,},tab,backspace,A-D */
+ 'F', 'F', 0, '^', '%', '<', '>', '&', /* 0xc0-0xc7: E-F,XOR,^,%,<,>,& */
+ 0, '|', 0, ':', '%', ' ', '@', '!', /* 0xc8-0xcf: &&,|,||,:,#, ,@,! */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0-0xd7: Memory Store,Recall,Clear,Add,Subtract,Muliply,Divide,+/- */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd8-0xdf: Clear,ClearEntry,Binary,Octal,Decimal,Hexadecimal */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0-0xe7: Left Ctrl,Shift,Alt,GUI, Right Ctrl,Shift,Alt,GUI */
+#endif
+};
+#endif /* CONFIG_HIDKBD_RAWSCANCODES */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_takesem
+ *
+ * Description:
+ * This is just a wrapper to handle the annoying behavior of semaphore
+ * waits that return due to the receipt of a signal.
+ *
+ ****************************************************************************/
+
+static void usbhost_takesem(sem_t *sem)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(sem) != 0)
+ {
+ /* The only case that an error should occr here is if the wait was
+ * awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: usbhost_pollnotify
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static void usbhost_pollnotify(FAR struct usbhost_state_s *priv)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_HIDKBD_NPOLLWAITERS; i++)
+ {
+ struct pollfd *fds = priv->fds[i];
+ if (fds)
+ {
+ fds->revents |= (fds->events & POLLIN);
+ if (fds->revents != 0)
+ {
+ uvdbg("Report events: %02x\n", fds->revents);
+ sem_post(fds->sem);
+ }
+ }
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: usbhost_allocclass
+ *
+ * Description:
+ * This is really part of the logic that implements the create() method
+ * of struct usbhost_registry_s. This function allocates memory for one
+ * new class instance.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Values:
+ * On success, this function will return a non-NULL instance of struct
+ * usbhost_class_s. NULL is returned on failure; this function will
+ * will fail only if there are insufficient resources to create another
+ * USB host class instance.
+ *
+ ****************************************************************************/
+
+static inline FAR struct usbhost_state_s *usbhost_allocclass(void)
+{
+ FAR struct usbhost_state_s *priv;
+
+ DEBUGASSERT(!up_interrupt_context());
+ priv = (FAR struct usbhost_state_s *)kmalloc(sizeof(struct usbhost_state_s));
+ uvdbg("Allocated: %p\n", priv);;
+ return priv;
+}
+
+/****************************************************************************
+ * Name: usbhost_freeclass
+ *
+ * Description:
+ * Free a class instance previously allocated by usbhost_allocclass().
+ *
+ * Input Parameters:
+ * class - A reference to the class instance to be freed.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline void usbhost_freeclass(FAR struct usbhost_state_s *class)
+{
+ DEBUGASSERT(class != NULL);
+
+ /* Free the class instance. */
+
+ uvdbg("Freeing: %p\n", class);;
+ kfree(class);
+}
+
+/****************************************************************************
+ * Name: Device name management
+ *
+ * Description:
+ * Some tiny functions to coordinate management of device names.
+ *
+ ****************************************************************************/
+
+static int usbhost_allocdevno(FAR struct usbhost_state_s *priv)
+{
+ irqstate_t flags;
+ int devno;
+
+ flags = irqsave();
+ for (devno = 0; devno < 26; devno++)
+ {
+ uint32_t bitno = 1 << devno;
+ if ((g_devinuse & bitno) == 0)
+ {
+ g_devinuse |= bitno;
+ priv->devchar = 'a' + devno;
+ irqrestore(flags);
+ return OK;
+ }
+ }
+
+ irqrestore(flags);
+ return -EMFILE;
+}
+
+static void usbhost_freedevno(FAR struct usbhost_state_s *priv)
+{
+ int devno = 'a' - priv->devchar;
+
+ if (devno >= 0 && devno < 26)
+ {
+ irqstate_t flags = irqsave();
+ g_devinuse &= ~(1 << devno);
+ irqrestore(flags);
+ }
+}
+
+static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, char *devname)
+{
+ (void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->devchar);
+}
+
+/****************************************************************************
+ * Name: usbhost_destroy
+ *
+ * Description:
+ * The USB device has been disconnected and the refernce count on the USB
+ * host class instance has gone to 1.. Time to destroy the USB host class
+ * instance.
+ *
+ * Input Parameters:
+ * arg - A reference to the class instance to be destroyed.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_destroy(FAR void *arg)
+{
+ FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg;
+ char devname[DEV_NAMELEN];
+
+ DEBUGASSERT(priv != NULL);
+ uvdbg("crefs: %d\n", priv->crefs);
+
+ /* Unregister the driver */
+
+ uvdbg("Unregister driver\n");
+ usbhost_mkdevname(priv, devname);
+ (void)unregister_driver(devname);
+
+ /* Release the device name used by this connection */
+
+ usbhost_freedevno(priv);
+
+ /* Free the interrupt endpoints */
+
+ if (priv->epin)
+ {
+ DRVR_EPFREE(priv->drvr, priv->epin);
+ }
+
+ if (priv->epout)
+ {
+ DRVR_EPFREE(priv->drvr, priv->epout);
+ }
+
+ /* Free any transfer buffers */
+
+ usbhost_tdfree(priv);
+
+ /* Destroy the semaphores */
+
+ sem_destroy(&priv->exclsem);
+ sem_destroy(&priv->waitsem);
+
+ /* Disconnect the USB host device */
+
+ DRVR_DISCONNECT(priv->drvr);
+
+ /* And free the class instance. Hmmm.. this may execute on the worker
+ * thread and the work structure is part of what is getting freed. That
+ * should be okay because once the work contained is removed from the
+ * queue, it should not longer be accessed by the worker thread.
+ */
+
+ usbhost_freeclass(priv);
+}
+
+/****************************************************************************
+ * Name: usbhost_mapscancode
+ *
+ * Description:
+ * Map a keyboard scancode to a printable ASCII character. There is no
+ * support here for function keys or cursor controls in this version of
+ * the driver.
+ *
+ * Input Parameters:
+ * scancode - Scan code to be mapped.
+ * modifier - Ctrl,Alt,Shift,GUI modifier bits
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline uint8_t usbhost_mapscancode(uint8_t scancode, uint8_t modifier)
+{
+#ifndef CONFIG_HIDKBD_RAWSCANCODES
+ /* Range check */
+
+ if (scancode >= USBHID_NUMSCANCODES)
+ {
+ return 0;
+ }
+
+ /* Is either shift key pressed? */
+
+ if ((modifier & (USBHID_MODIFER_LSHIFT|USBHID_MODIFER_RSHIFT)) != 0)
+ {
+ return ucmap[scancode];
+ }
+ else
+ {
+ return lcmap[scancode];
+ }
+#else
+ return scancode;
+#endif
+}
+
+/****************************************************************************
+ * Name: usbhost_kbdpoll
+ *
+ * Description:
+ * Periodically check for new keyboard data.
+ *
+ * Input Parameters:
+ * arg - A reference to the class instance to be destroyed.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static int usbhost_kbdpoll(int argc, char *argv[])
+{
+ FAR struct usbhost_state_s *priv;
+ FAR struct usb_ctrlreq_s *ctrlreq;
+#ifndef CONFIG_HIDKBD_NODEBOUNCE
+ uint8_t lastkey[6] = {0, 0, 0, 0, 0, 0};
+#endif
+#if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_VERBOSE)
+ unsigned int npolls = 0;
+#endif
+ unsigned int nerrors = 0;
+ int ret;
+
+ uvdbg("Started\n");
+
+ /* Synchronize with the start-up logic. Get the private instance, re-start
+ * the start-up logic, and wait a bit to make sure that all of the class
+ * creation logic has a chance to run to completion.
+ *
+ * NOTE: that the reference count is incremented here. Therefore, we know
+ * that the driver data structure will remain stable while this thread is
+ * running.
+ */
+
+ priv = g_priv;
+ DEBUGASSERT(priv != NULL);
+
+ priv->polling = true;
+ priv->crefs++;
+ usbhost_givesem(&g_syncsem);
+ sleep(1);
+
+ /* Loop here until the device is disconnected */
+
+ uvdbg("Entering poll loop\n");
+ while (!priv->disconnected)
+ {
+ /* Make sure that we have exclusive access to the private data
+ * structure. There may now be other tasks with the character driver
+ * open and actively trying to interact with the class driver.
+ */
+
+ usbhost_takesem(&priv->exclsem);
+
+ /* Format the HID report request:
+ *
+ * bmRequestType 10100001
+ * bRequest GET_REPORT (0x01)
+ * wValue Report Type and Report Index
+ * wIndex Interface Number
+ * wLength Descriptor Length
+ * Data Descriptor Data
+ */
+
+ ctrlreq = (struct usb_ctrlreq_s *)priv->tbuffer;
+ ctrlreq->type = USB_REQ_DIR_IN|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE;
+ ctrlreq->req = USBHID_REQUEST_GETREPORT;
+
+ usbhost_putle16(ctrlreq->value, (USBHID_REPORTTYPE_INPUT << 8));
+ usbhost_putle16(ctrlreq->index, priv->ifno);
+ usbhost_putle16(ctrlreq->len, sizeof(struct usbhid_kbdreport_s));
+
+ /* Send HID report request */
+
+ ret = DRVR_CTRLIN(priv->drvr, ctrlreq, priv->tbuffer);
+ usbhost_givesem(&priv->exclsem);
+
+ /* Check for errors -- Bail if an excessive number of errors
+ * are encountered.
+ */
+
+ if (ret != OK)
+ {
+ nerrors++;
+ udbg("ERROR: GETREPORT/INPUT, DRVR_CTRLIN returned: %d/%d\n",
+ ret, nerrors);
+
+ if (nerrors > 200)
+ {
+ udbg("Too many errors... aborting: %d\n", nerrors);
+ break;
+ }
+ }
+
+ /* The report was received correctly. But ignore the keystrokes if no
+ * task has opened the driver.
+ */
+
+ else if (priv->open)
+ {
+ struct usbhid_kbdreport_s *rpt = (struct usbhid_kbdreport_s *)priv->tbuffer;
+ unsigned int head;
+ unsigned int tail;
+ uint8_t ascii;
+ int i;
+
+ /* Add the newly received keystrokes to our internal buffer */
+
+ usbhost_takesem(&priv->exclsem);
+ head = priv->headndx;
+ tail = priv->tailndx;
+
+ for (i = 0; i < 6; i++)
+ {
+ /* Is this key pressed? But not pressed last time?
+ * HID spec: "The order of keycodes in array fields has no
+ * significance. Order determination is done by the host
+ * software comparing the contents of the previous report to
+ * the current report. If two or more keys are reported in
+ * one report, their order is indeterminate. Keyboards may
+ * buffer events that would have otherwise resulted in
+ * multiple event in a single report.
+ *
+ * "'Repeat Rate' and 'Delay Before First Repeat' are
+ * implemented by the host and not in the keyboard (this
+ * means the BIOS in legacy mode). The host may use the
+ * device report rate and the number of reports to determine
+ * how long a key is being held down. Alternatively, the host
+ * may use its own clock or the idle request for the timing
+ * of these features."
+ */
+
+ if (rpt->key[i] != USBHID_KBDUSE_NONE
+#ifndef CONFIG_HIDKBD_NODEBOUNCE
+ && rpt->key[i] != lastkey[i]
+#endif
+ )
+ {
+ /* Yes.. Add it to the buffer. */
+
+ /* Map the keyboard scancode to a printable ASCII
+ * character. There is no support here for function keys
+ * or cursor controls in this version of the driver.
+ */
+
+ ascii = usbhost_mapscancode(rpt->key[i], rpt->modifier);
+ uvdbg("Key %d: %02x ASCII:%c modifier: %02x\n",
+ i, rpt->key[i], ascii ? ascii : ' ', rpt->modifier);
+
+ /* Zero at this point means that the key does not map to a
+ * printable character.
+ */
+
+ if (ascii != 0)
+ {
+ /* Handle control characters. Zero after this means
+ * a valid, NUL character.
+ */
+
+ if ((rpt->modifier & (USBHID_MODIFER_LCTRL|USBHID_MODIFER_RCTRL)) != 0)
+ {
+ ascii &= 0x1f;
+ }
+
+ /* Copy the next keyboard character into the user
+ * buffer.
+ */
+
+ priv->kbdbuffer[head] = ascii;
+
+ /* Increment the head index */
+
+ if (++head >= CONFIG_HIDKBD_BUFSIZE)
+ {
+ head = 0;
+ }
+
+ /* If the buffer is full, then increment the tail
+ * index to make space. Is it better to lose old
+ * keystrokes or new?
+ */
+
+ if (tail == head)
+ {
+ if (++tail >= CONFIG_HIDKBD_BUFSIZE)
+ {
+ tail = 0;
+ }
+ }
+ }
+ }
+ /* Save the scancode (or lack thereof) for key debouncing on
+ * next keyboard report.
+ */
+
+#ifndef CONFIG_HIDKBD_NODEBOUNCE
+ lastkey[i] = rpt->key[i];
+#endif
+ }
+
+ /* Did we just transition from no data available to data available? */
+
+ if (head != tail && priv->headndx == priv->tailndx)
+ {
+ /* Yes.. Is there a thread waiting for keyboard data now? */
+
+ if (priv->waiting)
+ {
+ /* Yes.. wake it up */
+
+ usbhost_givesem(&priv->waitsem);
+ priv->waiting = false;
+ }
+
+ /* And wake up any threads waiting for the POLLIN event */
+
+ usbhost_pollnotify(priv);
+ }
+
+ /* Update the head/tail indices */
+
+ priv->headndx = head;
+ priv->tailndx = tail;
+ usbhost_givesem(&priv->exclsem);
+ }
+
+ /* If USB debug is on, then provide some periodic indication that
+ * polling is still happening.
+ */
+
+#if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_VERBOSE)
+ npolls++;
+ if ((npolls & 31) == 0)
+ {
+ udbg("Still polling: %d\n", npolls);
+ }
+#endif
+ /* Wait for the required amount (or until a signal is received). We
+ * will wake up when either the delay elapses or we are signalled that
+ * the device has been disconnected.
+ */
+
+ usleep(CONFIG_HIDKBD_POLLUSEC);
+ }
+
+ /* We get here when the driver is removed.. or when too many errors have
+ * been encountered.
+ *
+ * Make sure that we have exclusive access to the private data structure.
+ * There may now be other tasks with the character driver open and actively
+ * trying to interact with the class driver.
+ */
+
+ usbhost_takesem(&priv->exclsem);
+
+ /* Indicate that we are no longer running and decrement the reference
+ * count help by this thread. If there are no other users of the class,
+ * we can destroy it now. Otherwise, we have to wait until the all
+ * of the file descriptors are closed.
+ */
+
+ udbg("Keyboard removed, polling halted\n");
+ priv->polling = false;
+ if (--priv->crefs < 2)
+ {
+ /* Destroy the instance (while we hold the semaphore!) */
+
+ usbhost_destroy(priv);
+ }
+ else
+ {
+ /* No, we will destroy the driver instance when it is finally closed */
+
+ usbhost_givesem(&priv->exclsem);
+ }
+ return 0;
+}
+
+/****************************************************************************
+ * Name: usbhost_cfgdesc
+ *
+ * Description:
+ * This function implements the connect() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to provide the device's configuration
+ * descriptor to the class so that the class may initialize properly
+ *
+ * Input Parameters:
+ * priv - The USB host class instance.
+ * configdesc - A pointer to a uint8_t buffer container the configuration descripor.
+ * desclen - The length in bytes of the configuration descriptor.
+ * funcaddr - The USB address of the function containing the endpoint that EP0
+ * controls
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value is
+ * returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * This function will *not* be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr)
+{
+ FAR struct usb_cfgdesc_s *cfgdesc;
+ FAR struct usb_desc_s *desc;
+ FAR struct usbhost_epdesc_s epindesc;
+ FAR struct usbhost_epdesc_s epoutdesc;
+ int remaining;
+ uint8_t found = 0;
+ bool done = false;
+ int ret;
+
+ DEBUGASSERT(priv != NULL &&
+ configdesc != NULL &&
+ desclen >= sizeof(struct usb_cfgdesc_s));
+
+ /* Verify that we were passed a configuration descriptor */
+
+ cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc;
+ if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
+ {
+ return -EINVAL;
+ }
+
+ /* Get the total length of the configuration descriptor (little endian).
+ * It might be a good check to get the number of interfaces here too.
+ */
+
+ remaining = (int)usbhost_getle16(cfgdesc->totallen);
+
+ /* Skip to the next entry descriptor */
+
+ configdesc += cfgdesc->len;
+ remaining -= cfgdesc->len;
+
+ /* Loop where there are more dscriptors to examine */
+
+ while (remaining >= sizeof(struct usb_desc_s) && !done)
+ {
+ /* What is the next descriptor? */
+
+ desc = (FAR struct usb_desc_s *)configdesc;
+ switch (desc->type)
+ {
+ /* Interface descriptor. We really should get the number of endpoints
+ * from this descriptor too.
+ */
+
+ case USB_DESC_TYPE_INTERFACE:
+ {
+ FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)configdesc;
+
+ uvdbg("Interface descriptor\n");
+ DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC);
+
+ /* Did we already find what we needed from a preceding interface? */
+
+ if ((found & USBHOST_RQDFOUND) == USBHOST_RQDFOUND)
+ {
+ /* Yes.. then break out of the loop and use the preceding
+ * interface.
+ */
+
+ done = true;
+ }
+ else
+ {
+ /* Otherwise, save the interface number and discard any
+ * endpoints previously found
+ */
+
+ priv->ifno = ifdesc->ifno;
+ found = USBHOST_IFFOUND;
+ }
+ }
+ break;
+
+ /* HID descriptor */
+
+ case USBHID_DESCTYPE_HID:
+ uvdbg("HID descriptor\n");
+ break;
+
+ /* Endpoint descriptor. We expect one or two interrupt endpoints,
+ * a required IN endpoint and an optional OUT endpoint.
+ */
+
+ case USB_DESC_TYPE_ENDPOINT:
+ {
+ FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)configdesc;
+
+ uvdbg("Endpoint descriptor\n");
+ DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC);
+
+ /* Check for an interrupt endpoint. */
+
+ if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_INT)
+ {
+ /* Yes.. it is a interrupt endpoint. IN or OUT? */
+
+ if (USB_ISEPOUT(epdesc->addr))
+ {
+ /* It is an interrupt OUT endpoint. There not be more than one
+ * interrupt OUT endpoint.
+ */
+
+ if ((found & USBHOST_EPOUTFOUND) != 0)
+ {
+ /* Oops.. more than one endpoint. We don't know what to do with this. */
+
+ return -EINVAL;
+ }
+ found |= USBHOST_EPOUTFOUND;
+
+ /* Save the interrupt OUT endpoint information */
+
+ epoutdesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
+ epoutdesc.in = false;
+ epoutdesc.funcaddr = funcaddr;
+ epoutdesc.xfrtype = USB_EP_ATTR_XFER_INT;
+ epoutdesc.interval = epdesc->interval;
+ epoutdesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
+ uvdbg("Interrupt OUT EP addr:%d mxpacketsize:%d\n",
+ epoutdesc.addr, epoutdesc.mxpacketsize);
+ }
+ else
+ {
+ /* It is an interrupt IN endpoint. There should be only
+ * one interrupt IN endpoint.
+ */
+
+ if ((found & USBHOST_EPINFOUND) != 0)
+ {
+ /* Oops.. more than one endpint. We don't know what
+ * to do with this.
+ */
+
+ return -EINVAL;
+ }
+ found |= USBHOST_EPINFOUND;
+
+ /* Save the interrupt IN endpoint information */
+
+ epindesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
+ epindesc.in = 1;
+ epindesc.funcaddr = funcaddr;
+ epindesc.xfrtype = USB_EP_ATTR_XFER_INT;
+ epindesc.interval = epdesc->interval;
+ epindesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
+ uvdbg("Interrupt IN EP addr:%d mxpacketsize:%d\n",
+ epindesc.addr, epindesc.mxpacketsize);
+ }
+ }
+ }
+ break;
+
+ /* Other descriptors are just ignored for now */
+
+ default:
+ uvdbg("Other descriptor: %d\n", desc->type);
+ break;
+ }
+
+ /* What we found everything that we are going to find? */
+
+ if (found == USBHOST_ALLFOUND)
+ {
+ /* Yes.. then break out of the loop and use the preceding interface */
+
+ done = true;
+ }
+
+ /* Increment the address of the next descriptor */
+
+ configdesc += desc->len;
+ remaining -= desc->len;
+ }
+
+ /* Sanity checking... did we find all of things that we need? */
+
+ if ((found & USBHOST_RQDFOUND) != USBHOST_RQDFOUND)
+ {
+ ulldbg("ERROR: Found IF:%s EPIN:%s\n",
+ (found & USBHOST_IFFOUND) != 0 ? "YES" : "NO",
+ (found & USBHOST_EPINFOUND) != 0 ? "YES" : "NO");
+ return -EINVAL;
+ }
+
+ /* We are good... Allocate the endpoints. First, the required interrupt
+ * IN endpoint.
+ */
+
+ ret = DRVR_EPALLOC(priv->drvr, &epindesc, &priv->epin);
+ if (ret != OK)
+ {
+ udbg("ERROR: Failed to allocate interrupt IN endpoint\n");
+ return ret;
+ }
+
+ /* Then the optional interrupt OUT endpoint */
+
+ ullvdbg("Found EPOOUT:%s\n",
+ (found & USBHOST_EPOUTFOUND) != 0 ? "YES" : "NO");
+
+ if ((found & USBHOST_EPOUTFOUND) != 0)
+ {
+ ret = DRVR_EPALLOC(priv->drvr, &epoutdesc, &priv->epout);
+ if (ret != OK)
+ {
+ udbg("ERROR: Failed to allocate interrupt OUT endpoint\n");
+ (void)DRVR_EPFREE(priv->drvr, priv->epin);
+ return ret;
+ }
+ }
+
+ ullvdbg("Endpoints allocated\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbhost_devinit
+ *
+ * Description:
+ * The USB device has been successfully connected. This completes the
+ * initialization operations. It is first called after the
+ * configuration descriptor has been received.
+ *
+ * This function is called from the connect() method. This function always
+ * executes on the thread of the caller of connect().
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline int usbhost_devinit(FAR struct usbhost_state_s *priv)
+{
+ char devname[DEV_NAMELEN];
+ int ret;
+
+ /* Set aside a transfer buffer for exclusive use by the keyboard class driver */
+
+ ret = usbhost_tdalloc(priv);
+ if (ret != OK)
+ {
+ udbg("ERROR: Failed to allocate transfer buffer\n");
+ return ret;
+ }
+
+ /* Increment the reference count. This will prevent usbhost_destroy() from
+ * being called asynchronously if the device is removed.
+ */
+
+ priv->crefs++;
+ DEBUGASSERT(priv->crefs == 2);
+
+ /* Start a worker task to poll the USB device. It would be nice to used the
+ * the NuttX worker thread to do this, but this task needs to wait for events
+ * and activities on the worker thread should not involve significant waiting.
+ * Having a dedicated thread is more efficient in this sense, but requires more
+ * memory resources, primarily for the dedicated stack (CONFIG_HIDKBD_STACKSIZE).
+ */
+
+ uvdbg("user_start: Start poll task\n");
+
+ /* The inputs to a task started by task_create() are very awkard for this
+ * purpose. They are really designed for command line tasks (argc/argv). So
+ * the following is kludge pass binary data when the keyboard poll task
+ * is started.
+ *
+ * First, make sure we have exclusive access to g_priv (what is the likelihood
+ * of this being used? About zero, but we protect it anyway).
+ */
+
+ usbhost_takesem(&g_exclsem);
+ g_priv = priv;
+
+#ifndef CONFIG_CUSTOM_STACK
+ priv->pollpid = task_create("usbhost", CONFIG_HIDKBD_DEFPRIO,
+ CONFIG_HIDKBD_STACKSIZE,
+ (main_t)usbhost_kbdpoll, (const char **)NULL);
+#else
+ priv->pollpid = task_create("usbhost", CONFIG_HIDKBD_DEFPRIO,
+ (main_t)usbhost_kbdpoll, (const char **)NULL);
+#endif
+ if (priv->pollpid == ERROR)
+ {
+ /* Failed to started the poll thread... probably due to memory resources */
+
+ usbhost_givesem(&g_exclsem);
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ /* Now wait for the poll task to get properly initialized */
+
+ usbhost_takesem(&g_syncsem);
+ usbhost_givesem(&g_exclsem);
+
+ /* Register the driver */
+
+ uvdbg("Register driver\n");
+ usbhost_mkdevname(priv, devname);
+ ret = register_driver(devname, &usbhost_fops, 0666, priv);
+
+ /* We now have to be concerned about asynchronous modification of crefs
+ * because the driver has been registerd.
+ */
+
+errout:
+ usbhost_takesem(&priv->exclsem);
+ priv->crefs--;
+ usbhost_givesem(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_getle16
+ *
+ * Description:
+ * Get a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the little endian value.
+ *
+ * Returned Values:
+ * A uint16_t representing the whole 16-bit integer value
+ *
+ ****************************************************************************/
+
+static inline uint16_t usbhost_getle16(const uint8_t *val)
+{
+ return (uint16_t)val[1] << 8 | (uint16_t)val[0];
+}
+
+/****************************************************************************
+ * Name: usbhost_putle16
+ *
+ * Description:
+ * Put a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 16-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_putle16(uint8_t *dest, uint16_t val)
+{
+ dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
+ dest[1] = val >> 8;
+}
+
+/****************************************************************************
+ * Name: usbhost_getle32
+ *
+ * Description:
+ * Get a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the big endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline uint32_t usbhost_getle32(const uint8_t *val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ return (uint32_t)usbhost_getle16(&val[2]) << 16 | (uint32_t)usbhost_getle16(val);
+}
+
+/****************************************************************************
+ * Name: usbhost_putle32
+ *
+ * Description:
+ * Put a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+#if 0 /* Not used */
+static void usbhost_putle32(uint8_t *dest, uint32_t val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ usbhost_putle16(dest, (uint16_t)(val & 0xffff));
+ usbhost_putle16(dest+2, (uint16_t)(val >> 16));
+}
+#endif
+
+/****************************************************************************
+ * Name: usbhost_tdalloc
+ *
+ * Description:
+ * Allocate transfer buffer memory.
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * On sucess, zero (OK) is returned. On failure, an negated errno value
+ * is returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int usbhost_tdalloc(FAR struct usbhost_state_s *priv)
+{
+ DEBUGASSERT(priv && priv->tbuffer == NULL);
+ return DRVR_ALLOC(priv->drvr, &priv->tbuffer, &priv->tbuflen);
+}
+
+/****************************************************************************
+ * Name: usbhost_tdfree
+ *
+ * Description:
+ * Free transfer buffer memory.
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * On sucess, zero (OK) is returned. On failure, an negated errno value
+ * is returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int usbhost_tdfree(FAR struct usbhost_state_s *priv)
+{
+ int result = OK;
+ DEBUGASSERT(priv);
+
+ if (priv->tbuffer)
+ {
+ DEBUGASSERT(priv->drvr);
+ result = DRVR_FREE(priv->drvr, priv->tbuffer);
+ priv->tbuffer = NULL;
+ priv->tbuflen = 0;
+ }
+ return result;
+}
+
+/****************************************************************************
+ * struct usbhost_registry_s methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_create
+ *
+ * Description:
+ * This function implements the create() method of struct usbhost_registry_s.
+ * The create() method is a callback into the class implementation. It is
+ * used to (1) create a new instance of the USB host class state and to (2)
+ * bind a USB host driver "session" to the class instance. Use of this
+ * create() method will support environments where there may be multiple
+ * USB ports and multiple USB devices simultaneously connected.
+ *
+ * Input Parameters:
+ * drvr - An instance of struct usbhost_driver_s that the class
+ * implementation will "bind" to its state structure and will
+ * subsequently use to communicate with the USB host driver.
+ * id - In the case where the device supports multiple base classes,
+ * subclasses, or protocols, this specifies which to configure for.
+ *
+ * Returned Values:
+ * On success, this function will return a non-NULL instance of struct
+ * usbhost_class_s that can be used by the USB host driver to communicate
+ * with the USB host class. NULL is returned on failure; this function
+ * will fail only if the drvr input parameter is NULL or if there are
+ * insufficient resources to create another USB host class instance.
+ *
+ ****************************************************************************/
+
+static FAR struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *drvr,
+ FAR const struct usbhost_id_s *id)
+{
+ FAR struct usbhost_state_s *priv;
+
+ /* Allocate a USB host class instance */
+
+ priv = usbhost_allocclass();
+ if (priv)
+ {
+ /* Initialize the allocated storage class instance */
+
+ memset(priv, 0, sizeof(struct usbhost_state_s));
+
+ /* Assign a device number to this class instance */
+
+ if (usbhost_allocdevno(priv) == OK)
+ {
+ /* Initialize class method function pointers */
+
+ priv->class.connect = usbhost_connect;
+ priv->class.disconnected = usbhost_disconnected;
+
+ /* The initial reference count is 1... One reference is held by the driver */
+
+ priv->crefs = 1;
+
+ /* Initialize semaphores */
+
+ sem_init(&priv->exclsem, 0, 1);
+ sem_init(&priv->waitsem, 0, 0);
+
+ /* Bind the driver to the storage class instance */
+
+ priv->drvr = drvr;
+
+ /* Return the instance of the USB keyboard class driver */
+
+ return &priv->class;
+ }
+ }
+
+ /* An error occurred. Free the allocation and return NULL on all failures */
+
+ if (priv)
+ {
+ usbhost_freeclass(priv);
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ * struct usbhost_class_s methods
+ ****************************************************************************/
+/****************************************************************************
+ * Name: usbhost_connect
+ *
+ * Description:
+ * This function implements the connect() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to provide the device's configuration
+ * descriptor to the class so that the class may initialize properly
+ *
+ * Input Parameters:
+ * class - The USB host class entry previously obtained from a call to create().
+ * configdesc - A pointer to a uint8_t buffer container the configuration descripor.
+ * desclen - The length in bytes of the configuration descriptor.
+ * funcaddr - The USB address of the function containing the endpoint that EP0
+ * controls
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value is
+ * returned indicating the nature of the failure
+ *
+ * NOTE that the class instance remains valid upon return with a failure. It is
+ * the responsibility of the higher level enumeration logic to call
+ * CLASS_DISCONNECTED to free up the class driver resources.
+ *
+ * Assumptions:
+ * - This function will *not* be called from an interrupt handler.
+ * - If this function returns an error, the USB host controller driver
+ * must call to DISCONNECTED method to recover from the error
+ *
+ ****************************************************************************/
+
+static int usbhost_connect(FAR struct usbhost_class_s *class,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr)
+{
+ FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class;
+ int ret;
+
+ DEBUGASSERT(priv != NULL &&
+ configdesc != NULL &&
+ desclen >= sizeof(struct usb_cfgdesc_s));
+
+ /* Parse the configuration descriptor to get the endpoints */
+
+ ret = usbhost_cfgdesc(priv, configdesc, desclen, funcaddr);
+ if (ret != OK)
+ {
+ udbg("usbhost_cfgdesc() failed: %d\n", ret);
+ }
+ else
+ {
+ /* Now configure the device and register the NuttX driver */
+
+ ret = usbhost_devinit(priv);
+ if (ret != OK)
+ {
+ udbg("usbhost_devinit() failed: %d\n", ret);
+ }
+ }
+
+ /* ERROR handling: Do nothing. If we return and error during connection,
+ * the driver is required to call the DISCONNECT method. Possibilities:
+ *
+ * - Failure occurred before the kbdpoll task was started successfully.
+ * In this case, the disconnection will have to be handled on the worker
+ * task.
+ * - Failure occured after the kbdpoll task was started succesffuly. In
+ * this case, the disconnetion can be performed on the kbdpoll thread.
+ */
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_disconnected
+ *
+ * Description:
+ * This function implements the disconnected() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to inform the class that the USB device has
+ * been disconnected.
+ *
+ * Input Parameters:
+ * class - The USB host class entry previously obtained from a call to
+ * create().
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value
+ * is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * This function may be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int usbhost_disconnected(struct usbhost_class_s *class)
+{
+ FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class;
+
+ DEBUGASSERT(priv != NULL);
+
+ /* Set an indication to any users of the keyboard device that the device
+ * is no longer available.
+ */
+
+ priv->disconnected = true;
+ ullvdbg("Disconnected\n");
+
+ /* Is there a thread waiting for keyboard data that will never come? */
+
+ if (priv->waiting)
+ {
+ /* Yes.. wake it up */
+
+ usbhost_givesem(&priv->waitsem);
+ priv->waiting = false;
+ }
+
+ /* Possibilities:
+ *
+ * - Failure occurred before the kbdpoll task was started successfully.
+ * In this case, the disconnection will have to be handled on the worker
+ * task.
+ * - Failure occured after the kbdpoll task was started succesffuly. In
+ * this case, the disconnetion can be performed on the kbdpoll thread.
+ */
+
+ if (priv->polling)
+ {
+ /* The polling task is still alive. Signal the keyboard polling task.
+ * When that task wakes up, it will decrement the reference count and,
+ * perhaps, destroy the class instance. Then it will exit.
+ */
+
+ (void)kill(priv->pollpid, SIGALRM);
+ }
+ else
+ {
+ /* In the case where the failure occurs before the polling task was
+ * started. Now what? We are probably executing from an interrupt
+ * handler here. We will use the worker thread. This is kind of
+ * wasteful and begs for a re-design.
+ */
+
+ DEBUGASSERT(priv->work.worker == NULL);
+ (void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Character driver methods
+ ****************************************************************************/
+/****************************************************************************
+ * Name: usbhost_open
+ *
+ * Description:
+ * Standard character driver open method.
+ *
+ ****************************************************************************/
+
+static int usbhost_open(FAR struct file *filep)
+{
+ FAR struct inode *inode;
+ FAR struct usbhost_state_s *priv;
+ irqstate_t flags;
+ int ret;
+
+ uvdbg("Entry\n");
+ DEBUGASSERT(filep && filep->f_inode);
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ /* Make sure that we have exclusive access to the private data structure */
+
+ DEBUGASSERT(priv && priv->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS);
+ usbhost_takesem(&priv->exclsem);
+
+ /* Check if the keyboard device is still connected. We need to disable
+ * interrupts momentarily to assure that there are no asynchronous disconnect
+ * events.
+ */
+
+ flags = irqsave();
+ if (priv->disconnected)
+ {
+ /* No... the driver is no longer bound to the class. That means that
+ * the USB storage device is no longer connected. Refuse any further
+ * attempts to open the driver.
+ */
+
+ ret = -ENODEV;
+ }
+ else
+ {
+ /* Otherwise, just increment the reference count on the driver */
+
+ priv->crefs++;
+ priv->open = true;
+ ret = OK;
+ }
+ irqrestore(flags);
+
+ usbhost_givesem(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_close
+ *
+ * Description:
+ * Standard character driver close method.
+ *
+ ****************************************************************************/
+
+static int usbhost_close(FAR struct file *filep)
+{
+ FAR struct inode *inode;
+ FAR struct usbhost_state_s *priv;
+
+ uvdbg("Entry\n");
+ DEBUGASSERT(filep && filep->f_inode);
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ /* Decrement the reference count on the driver */
+
+ DEBUGASSERT(priv->crefs > 1);
+ usbhost_takesem(&priv->exclsem);
+ priv->crefs--;
+
+ /* Is this the last reference (other than the one held by the USB host
+ * controller driver)
+ */
+
+ if (priv->crefs <= 1)
+ {
+ irqstate_t flags;
+
+ /* Yes.. then the driver is no longer open */
+
+ priv->open = false;
+ priv->headndx = 0;
+ priv->tailndx = 0;
+
+ /* We need to disable interrupts momentarily to assure that there are
+ * no asynchronous disconnect events.
+ */
+
+ flags = irqsave();
+
+ /* Check if the USB keyboard device is still connected. If the device is
+ * no longer connected, then unregister the driver and free the driver
+ * class instance.
+ */
+
+ if (priv->disconnected)
+ {
+ /* Destroy the class instance (we can't use priv after this; we can't
+ * 'give' the semapore)
+ */
+
+ usbhost_destroy(priv);
+ irqrestore(flags);
+ return OK;
+ }
+ irqrestore(flags);
+ }
+
+ usbhost_givesem(&priv->exclsem);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbhost_read
+ *
+ * Description:
+ * Standard character driver read method.
+ *
+ ****************************************************************************/
+
+static ssize_t usbhost_read(FAR struct file *filep, FAR char *buffer, size_t len)
+{
+ FAR struct inode *inode;
+ FAR struct usbhost_state_s *priv;
+ size_t nbytes;
+ unsigned int tail;
+ int ret;
+
+ uvdbg("Entry\n");
+ DEBUGASSERT(filep && filep->f_inode && buffer);
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ /* Make sure that we have exclusive access to the private data structure */
+
+ DEBUGASSERT(priv && priv->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS);
+ usbhost_takesem(&priv->exclsem);
+
+ /* Check if the keyboard is still connected. We need to disable interrupts
+ * momentarily to assure that there are no asynchronous disconnect events.
+ */
+
+ if (priv->disconnected)
+ {
+ /* No... the driver is no longer bound to the class. That means that
+ * the USB keybaord is no longer connected. Refuse any further attempts
+ * to access the driver.
+ */
+
+ ret = -ENODEV;
+ }
+ else
+ {
+ /* Is there keyboard data now? */
+
+ while (priv->tailndx == priv->headndx)
+ {
+ /* No.. were we open non-blocking? */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ /* Yes.. then return a failure */
+
+ ret = -EAGAIN;
+ goto errout;
+ }
+
+ /* Wait for data to be available */
+
+ uvdbg("Waiting...\n");
+ priv->waiting = true;
+ usbhost_givesem(&priv->exclsem);
+ usbhost_takesem(&priv->waitsem);
+ usbhost_takesem(&priv->exclsem);
+
+ /* Did the keyboard become disconnected while we were waiting */
+
+ if (priv->disconnected)
+ {
+ ret = -ENODEV;
+ goto errout;
+ }
+ }
+
+ /* Read data from our internal buffer of received characters */
+
+ for (tail = priv->tailndx, nbytes = 0;
+ tail != priv->headndx && nbytes < len;
+ nbytes++)
+ {
+ /* Copy the next keyboard character into the user buffer */
+
+ *buffer++ = priv->kbdbuffer[tail];
+
+ /* Handle wrap-around of the tail index */
+
+ if (++tail >= CONFIG_HIDKBD_BUFSIZE)
+ {
+ tail = 0;
+ }
+ }
+ ret = nbytes;
+
+ /* Update the tail index (pehaps marking the buffer empty) */
+
+ priv->tailndx = tail;
+ }
+
+errout:
+ usbhost_givesem(&priv->exclsem);
+ return (ssize_t)ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_write
+ *
+ * Description:
+ * Standard character driver write method.
+ *
+ ****************************************************************************/
+
+static ssize_t usbhost_write(FAR struct file *filep, FAR const char *buffer, size_t len)
+{
+ /* We won't try to write to the keyboard */
+
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: usbhost_poll
+ *
+ * Description:
+ * Standard character driver poll method.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static int usbhost_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup)
+{
+ FAR struct inode *inode;
+ FAR struct usbhost_state_s *priv;
+ int ret = OK;
+ int i;
+
+ uvdbg("Entry\n");
+ DEBUGASSERT(filep && filep->f_inode && fds);
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ /* Make sure that we have exclusive access to the private data structure */
+
+ DEBUGASSERT(priv);
+ usbhost_takesem(&priv->exclsem);
+
+ /* Check if the keyboard is still connected. We need to disable interrupts
+ * momentarily to assure that there are no asynchronous disconnect events.
+ */
+
+ if (priv->disconnected)
+ {
+ /* No... the driver is no longer bound to the class. That means that
+ * the USB keybaord is no longer connected. Refuse any further attempts
+ * to access the driver.
+ */
+
+ ret = -ENODEV;
+ }
+ else if (setup)
+ {
+ /* This is a request to set up the poll. Find an availableslot for
+ * the poll structure reference
+ */
+
+ for (i = 0; i < CONFIG_HIDKBD_NPOLLWAITERS; i++)
+ {
+ /* Find an available slot */
+
+ if (!priv->fds[i])
+ {
+ /* Bind the poll structure and this slot */
+
+ priv->fds[i] = fds;
+ fds->priv = &priv->fds[i];
+ break;
+ }
+ }
+
+ if (i >= CONFIG_HIDKBD_NPOLLWAITERS)
+ {
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto errout;
+ }
+
+ /* Should we immediately notify on any of the requested events? Notify
+ * the POLLIN event if there is buffered keyboard data.
+ */
+
+ if (priv->headndx != priv->tailndx)
+ {
+ usbhost_pollnotify(priv);
+ }
+ }
+ else
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+ DEBUGASSERT(slot);
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+errout:
+ sem_post(&priv->exclsem);
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_kbdinit
+ *
+ * Description:
+ * Initialize the USB storage HID keyboard class driver. This function
+ * should be called be platform-specific code in order to initialize and
+ * register support for the USB host HID keyboard class device.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Values:
+ * On success this function will return zero (OK); A negated errno value
+ * will be returned on failure.
+ *
+ ****************************************************************************/
+
+int usbhost_kbdinit(void)
+{
+ /* Perform any one-time initialization of the class implementation */
+
+ sem_init(&g_exclsem, 0, 1);
+ sem_init(&g_syncsem, 0, 0);
+
+ /* Advertise our availability to support (certain) devices */
+
+ return usbhost_registerclass(&g_skeleton);
+}
+
+#endif /* CONFIG_USBHOST)&& !CONFIG_USBHOST_INT_DISABLE && CONFIG_NFILE_DESCRIPTORS */
+
+
diff --git a/nuttx/drivers/usbhost/usbhost_registerclass.c b/nuttx/drivers/usbhost/usbhost_registerclass.c
new file mode 100644
index 000000000..f4d1b64af
--- /dev/null
+++ b/nuttx/drivers/usbhost/usbhost_registerclass.c
@@ -0,0 +1,117 @@
+/****************************************************************************
+ * drivers/usbhost/usbhost_registerclass.c
+ *
+ * Copyright (C) 2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/irq.h>
+#include <nuttx/usb/usbhost.h>
+
+#include "usbhost_registry.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_registerclass
+ *
+ * Description:
+ * Register a USB host class implementation. The caller provides an
+ * instance of struct usbhost_registry_s that contains all of the
+ * information that will be needed later to (1) associate the USB host
+ * class implementation with a connected USB device, and (2) to obtain and
+ * bind a struct usbhost_class_s instance for the device.
+ *
+ * Input Parameters:
+ * class - An write-able instance of struct usbhost_registry_s that will be
+ * maintained in a registry.
+ *
+ * Returned Values:
+ * On success, this function will return zero (OK). Otherwise, a negated
+ * errno value is returned.
+ *
+ ****************************************************************************/
+
+int usbhost_registerclass(struct usbhost_registry_s *class)
+{
+ irqstate_t flags;
+
+ uvdbg("Registering class:%p nids:%d\n", class, class->nids);
+
+ /* g_classregistry is a singly-linkedlist of class ID information added by
+ * calls to usbhost_registerclass(). Since this list is accessed from USB
+ * host controller interrupt handling logic, accesses to this list must be
+ * protected by disabling interrupts.
+ */
+
+ flags = irqsave();
+
+ /* Add the new class ID info to the head of the list */
+
+ class->flink = g_classregistry;
+ g_classregistry = class;
+
+ irqrestore(flags);
+ return OK;
+}
+
diff --git a/nuttx/drivers/usbhost/usbhost_registry.c b/nuttx/drivers/usbhost/usbhost_registry.c
new file mode 100644
index 000000000..fb2e900e2
--- /dev/null
+++ b/nuttx/drivers/usbhost/usbhost_registry.c
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * drivers/usbhost/usbhost_registry.c
+ *
+ * Copyright (C) 2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/usb/usbhost.h>
+
+#include "usbhost_registry.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* g_classregistry is a singly-linkedlist of class ID information added by
+ * calls to usbhost_registerclass(). Since this list is accessed from USB
+ * host controller interrupt handling logic, accesses to this list must be
+ * protected by disabling interrupts.
+ */
+
+struct usbhost_registry_s *g_classregistry;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
diff --git a/nuttx/drivers/usbhost/usbhost_registry.h b/nuttx/drivers/usbhost/usbhost_registry.h
new file mode 100644
index 000000000..759a1c66e
--- /dev/null
+++ b/nuttx/drivers/usbhost/usbhost_registry.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+ * drivers/usbhost/usbdev_registry.h
+ *
+ * Copyright (C) 2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_USBHOST_USBHOST_REGISTRY_H
+#define __DRIVERS_USBHOST_USBHOST_REGISTRY_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/usb/usbhost.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+# define EXTERN extern "C"
+extern "C"
+{
+#else
+# define EXTERN extern
+#endif
+
+/* g_classregistry is a singly-linkedlist of class ID information added by
+ * calls to usbhost_registerclass(). Since this list is accessed from USB
+ * host controller interrupt handling logic, accesses to this list must be
+ * protected by disabling interrupts.
+ */
+
+EXTERN struct usbhost_registry_s *g_classregistry;
+
+/************************************************************************************
+ * Public Function Prototypes
+ ************************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* #define __DRIVERS_USBHOST_USBHOST_REGISTRY_H */
diff --git a/nuttx/drivers/usbhost/usbhost_skeleton.c b/nuttx/drivers/usbhost/usbhost_skeleton.c
new file mode 100644
index 000000000..c44a265e8
--- /dev/null
+++ b/nuttx/drivers/usbhost/usbhost_skeleton.c
@@ -0,0 +1,1060 @@
+/****************************************************************************
+ * drivers/usbhost/usbhost_skeleton.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbhost.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+# warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
+#endif
+
+/* Driver support ***********************************************************/
+/* This format is used to construct the /dev/skel[n] device driver path. It
+ * defined here so that it will be used consistently in all places.
+ */
+
+#define DEV_FORMAT "/dev/skel%c"
+#define DEV_NAMELEN 12
+
+/* Used in usbhost_cfgdesc() */
+
+#define USBHOST_IFFOUND 0x01
+#define USBHOST_BINFOUND 0x02
+#define USBHOST_BOUTFOUND 0x04
+#define USBHOST_ALLFOUND 0x07
+
+#define USBHOST_MAX_CREFS 0x7fff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure contains the internal, private state of the USB host class
+ * driver.
+ */
+
+struct usbhost_state_s
+{
+ /* This is the externally visible portion of the state */
+
+ struct usbhost_class_s class;
+
+ /* This is an instance of the USB host driver bound to this class instance */
+
+ struct usbhost_driver_s *drvr;
+
+ /* The remainder of the fields are provide to the class driver */
+
+ char devchar; /* Character identifying the /dev/skel[n] device */
+ volatile bool disconnected; /* TRUE: Device has been disconnected */
+ uint8_t ifno; /* Interface number */
+ int16_t crefs; /* Reference count on the driver instance */
+ sem_t exclsem; /* Used to maintain mutual exclusive access */
+ struct work_s work; /* For interacting with the worker thread */
+ FAR uint8_t *tbuffer; /* The allocated transfer buffer */
+ size_t tbuflen; /* Size of the allocated transfer buffer */
+ usbhost_ep_t epin; /* IN endpoint */
+ usbhost_ep_t epout; /* OUT endpoint */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Semaphores */
+
+static void usbhost_takesem(sem_t *sem);
+#define usbhost_givesem(s) sem_post(s);
+
+/* Memory allocation services */
+
+static inline FAR struct usbhost_state_s *usbhost_allocclass(void);
+static inline void usbhost_freeclass(FAR struct usbhost_state_s *class);
+
+/* Device name management */
+
+static int usbhost_allocdevno(FAR struct usbhost_state_s *priv);
+static void usbhost_freedevno(FAR struct usbhost_state_s *priv);
+static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, char *devname);
+
+/* Worker thread actions */
+
+static void usbhost_destroy(FAR void *arg);
+
+/* Helpers for usbhost_connect() */
+
+static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr);
+static inline int usbhost_devinit(FAR struct usbhost_state_s *priv);
+
+/* (Little Endian) Data helpers */
+
+static inline uint16_t usbhost_getle16(const uint8_t *val);
+static inline void usbhost_putle16(uint8_t *dest, uint16_t val);
+static inline uint32_t usbhost_getle32(const uint8_t *val);
+static void usbhost_putle32(uint8_t *dest, uint32_t val);
+
+/* Transfer descriptor memory management */
+
+static inline int usbhost_talloc(FAR struct usbhost_state_s *priv);
+static inline int usbhost_tfree(FAR struct usbhost_state_s *priv);
+
+/* struct usbhost_registry_s methods */
+
+static struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *drvr,
+ FAR const struct usbhost_id_s *id);
+
+/* struct usbhost_class_s methods */
+
+static int usbhost_connect(FAR struct usbhost_class_s *class,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr);
+static int usbhost_disconnected(FAR struct usbhost_class_s *class);
+
+/* Driver methods -- depend upon the type of NuttX driver interface exported */
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This structure provides the registry entry ID informatino that will be
+ * used to associate the USB class driver to a connected USB device.
+ */
+
+static const const struct usbhost_id_s g_id =
+{
+ 0, /* base -- Must be one of the USB_CLASS_* definitions in usb.h */
+ 0, /* subclass -- depends on the device */
+ 0, /* proto -- depends on the device */
+ 0, /* vid */
+ 0 /* pid */
+};
+
+/* This is the USB host storage class's registry entry */
+
+static struct usbhost_registry_s g_skeleton =
+{
+ NULL, /* flink */
+ usbhost_create, /* create */
+ 1, /* nids */
+ &g_id /* id[] */
+};
+
+/* This is a bitmap that is used to allocate device names /dev/skela-z. */
+
+static uint32_t g_devinuse;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_takesem
+ *
+ * Description:
+ * This is just a wrapper to handle the annoying behavior of semaphore
+ * waits that return due to the receipt of a signal.
+ *
+ ****************************************************************************/
+
+static void usbhost_takesem(sem_t *sem)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(sem) != 0)
+ {
+ /* The only case that an error should occr here is if the wait was
+ * awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: usbhost_allocclass
+ *
+ * Description:
+ * This is really part of the logic that implements the create() method
+ * of struct usbhost_registry_s. This function allocates memory for one
+ * new class instance.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Values:
+ * On success, this function will return a non-NULL instance of struct
+ * usbhost_class_s. NULL is returned on failure; this function will
+ * will fail only if there are insufficient resources to create another
+ * USB host class instance.
+ *
+ ****************************************************************************/
+
+static inline FAR struct usbhost_state_s *usbhost_allocclass(void)
+{
+ FAR struct usbhost_state_s *priv;
+
+ DEBUGASSERT(!up_interrupt_context());
+ priv = (FAR struct usbhost_state_s *)kmalloc(sizeof(struct usbhost_state_s));
+ uvdbg("Allocated: %p\n", priv);;
+ return priv;
+}
+
+/****************************************************************************
+ * Name: usbhost_freeclass
+ *
+ * Description:
+ * Free a class instance previously allocated by usbhost_allocclass().
+ *
+ * Input Parameters:
+ * class - A reference to the class instance to be freed.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline void usbhost_freeclass(FAR struct usbhost_state_s *class)
+{
+ DEBUGASSERT(class != NULL);
+
+ /* Free the class instance (perhaps calling sched_free() in case we are
+ * executing from an interrupt handler.
+ */
+
+ uvdbg("Freeing: %p\n", class);;
+ kfree(class);
+}
+
+/****************************************************************************
+ * Name: Device name management
+ *
+ * Description:
+ * Some tiny functions to coordinate management of device names.
+ *
+ ****************************************************************************/
+
+static int usbhost_allocdevno(FAR struct usbhost_state_s *priv)
+{
+ irqstate_t flags;
+ int devno;
+
+ flags = irqsave();
+ for (devno = 0; devno < 26; devno++)
+ {
+ uint32_t bitno = 1 << devno;
+ if ((g_devinuse & bitno) == 0)
+ {
+ g_devinuse |= bitno;
+ priv->devchar = 'a' + devno;
+ irqrestore(flags);
+ return OK;
+ }
+ }
+
+ irqrestore(flags);
+ return -EMFILE;
+}
+
+static void usbhost_freedevno(FAR struct usbhost_state_s *priv)
+{
+ int devno = 'a' - priv->devchar;
+
+ if (devno >= 0 && devno < 26)
+ {
+ irqstate_t flags = irqsave();
+ g_devinuse &= ~(1 << devno);
+ irqrestore(flags);
+ }
+}
+
+static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, char *devname)
+{
+ (void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->devchar);
+}
+
+/****************************************************************************
+ * Name: usbhost_destroy
+ *
+ * Description:
+ * The USB device has been disconnected and the refernce count on the USB
+ * host class instance has gone to 1.. Time to destroy the USB host class
+ * instance.
+ *
+ * Input Parameters:
+ * arg - A reference to the class instance to be destroyed.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_destroy(FAR void *arg)
+{
+ FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg;
+
+ DEBUGASSERT(priv != NULL);
+ uvdbg("crefs: %d\n", priv->crefs);
+
+ /* Unregister the driver */
+
+ /* Release the device name used by this connection */
+
+ usbhost_freedevno(priv);
+
+ /* Free the endpoints */
+
+ /* Free any transfer buffers */
+
+ /* Destroy the semaphores */
+
+ /* Disconnect the USB host device */
+
+ DRVR_DISCONNECT(priv->drvr);
+
+ /* And free the class instance. Hmmm.. this may execute on the worker
+ * thread and the work structure is part of what is getting freed. That
+ * should be okay because once the work contained is removed from the
+ * queue, it should not longer be accessed by the worker thread.
+ */
+
+ usbhost_freeclass(priv);
+}
+
+/****************************************************************************
+ * Name: usbhost_cfgdesc
+ *
+ * Description:
+ * This function implements the connect() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to provide the device's configuration
+ * descriptor to the class so that the class may initialize properly
+ *
+ * Input Parameters:
+ * priv - The USB host class instance.
+ * configdesc - A pointer to a uint8_t buffer container the configuration descripor.
+ * desclen - The length in bytes of the configuration descriptor.
+ * funcaddr - The USB address of the function containing the endpoint that EP0
+ * controls
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value is
+ * returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * This function will *not* be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr)
+{
+ FAR struct usb_cfgdesc_s *cfgdesc;
+ FAR struct usb_desc_s *desc;
+ FAR struct usbhost_epdesc_s bindesc;
+ FAR struct usbhost_epdesc_s boutdesc;
+ int remaining;
+ uint8_t found = 0;
+ int ret;
+
+ DEBUGASSERT(priv != NULL &&
+ configdesc != NULL &&
+ desclen >= sizeof(struct usb_cfgdesc_s));
+
+ /* Verify that we were passed a configuration descriptor */
+
+ cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc;
+ if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
+ {
+ return -EINVAL;
+ }
+
+ /* Get the total length of the configuration descriptor (little endian).
+ * It might be a good check to get the number of interfaces here too.
+ */
+
+ remaining = (int)usbhost_getle16(cfgdesc->totallen);
+
+ /* Skip to the next entry descriptor */
+
+ configdesc += cfgdesc->len;
+ remaining -= cfgdesc->len;
+
+ /* Loop where there are more dscriptors to examine */
+
+ while (remaining >= sizeof(struct usb_desc_s))
+ {
+ /* What is the next descriptor? */
+
+ desc = (FAR struct usb_desc_s *)configdesc;
+ switch (desc->type)
+ {
+ /* Interface descriptor. We really should get the number of endpoints
+ * from this descriptor too.
+ */
+
+ case USB_DESC_TYPE_INTERFACE:
+ {
+ FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)configdesc;
+
+ uvdbg("Interface descriptor\n");
+ DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC);
+
+ /* Save the interface number and mark ONLY the interface found */
+
+ priv->ifno = ifdesc->ifno;
+ found = USBHOST_IFFOUND;
+ }
+ break;
+
+ /* Endpoint descriptor. Here, we expect two bulk endpoints, an IN
+ * and an OUT.
+ */
+
+ case USB_DESC_TYPE_ENDPOINT:
+ {
+ FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)configdesc;
+
+ uvdbg("Endpoint descriptor\n");
+ DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC);
+
+ /* Check for a bulk endpoint. */
+
+ if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_BULK)
+ {
+ /* Yes.. it is a bulk endpoint. IN or OUT? */
+
+ if (USB_ISEPOUT(epdesc->addr))
+ {
+ /* It is an OUT bulk endpoint. There should be only one
+ * bulk OUT endpoint.
+ */
+
+ if ((found & USBHOST_BOUTFOUND) != 0)
+ {
+ /* Oops.. more than one endpoint. We don't know
+ * what to do with this.
+ */
+
+ return -EINVAL;
+ }
+ found |= USBHOST_BOUTFOUND;
+
+ /* Save the bulk OUT endpoint information */
+
+ boutdesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
+ boutdesc.in = false;
+ boutdesc.funcaddr = funcaddr;
+ boutdesc.xfrtype = USB_EP_ATTR_XFER_BULK;
+ boutdesc.interval = epdesc->interval;
+ boutdesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
+ uvdbg("Bulk OUT EP addr:%d mxpacketsize:%d\n",
+ boutdesc.addr, boutdesc.mxpacketsize);
+ }
+ else
+ {
+ /* It is an IN bulk endpoint. There should be only one
+ * bulk IN endpoint.
+ */
+
+ if ((found & USBHOST_BINFOUND) != 0)
+ {
+ /* Oops.. more than one endpoint. We don't know
+ * what to do with this.
+ */
+
+ return -EINVAL;
+ }
+ found |= USBHOST_BINFOUND;
+
+ /* Save the bulk IN endpoint information */
+
+ bindesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
+ bindesc.in = 1;
+ bindesc.funcaddr = funcaddr;
+ bindesc.xfrtype = USB_EP_ATTR_XFER_BULK;
+ bindesc.interval = epdesc->interval;
+ bindesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
+ uvdbg("Bulk IN EP addr:%d mxpacketsize:%d\n",
+ bindesc.addr, bindesc.mxpacketsize);
+ }
+ }
+ }
+ break;
+
+ /* Other descriptors are just ignored for now */
+
+ default:
+ break;
+ }
+
+ /* If we found everything we need with this interface, then break out
+ * of the loop early.
+ */
+
+ if (found == USBHOST_ALLFOUND)
+ {
+ break;
+ }
+
+ /* Increment the address of the next descriptor */
+
+ configdesc += desc->len;
+ remaining -= desc->len;
+ }
+
+ /* Sanity checking... did we find all of things that we need? */
+
+ if (found != USBHOST_ALLFOUND)
+ {
+ ulldbg("ERROR: Found IF:%s BIN:%s BOUT:%s\n",
+ (found & USBHOST_IFFOUND) != 0 ? "YES" : "NO",
+ (found & USBHOST_BINFOUND) != 0 ? "YES" : "NO",
+ (found & USBHOST_BOUTFOUND) != 0 ? "YES" : "NO");
+ return -EINVAL;
+ }
+
+ /* We are good... Allocate the endpoints */
+
+ ret = DRVR_EPALLOC(priv->drvr, &boutdesc, &priv->epout);
+ if (ret != OK)
+ {
+ udbg("ERROR: Failed to allocate Bulk OUT endpoint\n");
+ return ret;
+ }
+
+ ret = DRVR_EPALLOC(priv->drvr, &bindesc, &priv->epin);
+ if (ret != OK)
+ {
+ udbg("ERROR: Failed to allocate Bulk IN endpoint\n");
+ (void)DRVR_EPFREE(priv->drvr, priv->epout);
+ return ret;
+ }
+
+ ullvdbg("Endpoints allocated\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbhost_devinit
+ *
+ * Description:
+ * The USB device has been successfully connected. This completes the
+ * initialization operations. It is first called after the
+ * configuration descriptor has been received.
+ *
+ * This function is called from the connect() method. This function always
+ * executes on the thread of the caller of connect().
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline int usbhost_devinit(FAR struct usbhost_state_s *priv)
+{
+ int ret = OK;
+
+ /* Set aside a transfer buffer for exclusive use by the class driver */
+
+ /* Increment the reference count. This will prevent usbhost_destroy() from
+ * being called asynchronously if the device is removed.
+ */
+
+ priv->crefs++;
+ DEBUGASSERT(priv->crefs == 2);
+
+ /* Configure the device */
+
+ /* Register the driver */
+
+ if (ret == OK)
+ {
+ char devname[DEV_NAMELEN];
+
+ uvdbg("Register block driver\n");
+ usbhost_mkdevname(priv, devname);
+ // ret = register_blockdriver(devname, &g_bops, 0, priv);
+ }
+
+ /* Check if we successfully initialized. We now have to be concerned
+ * about asynchronous modification of crefs because the block
+ * driver has been registerd.
+ */
+
+ if (ret == OK)
+ {
+ usbhost_takesem(&priv->exclsem);
+ DEBUGASSERT(priv->crefs >= 2);
+
+ /* Handle a corner case where (1) open() has been called so the
+ * reference count is > 2, but the device has been disconnected.
+ * In this case, the class instance needs to persist until close()
+ * is called.
+ */
+
+ if (priv->crefs <= 2 && priv->disconnected)
+ {
+ /* We don't have to give the semaphore because it will be
+ * destroyed when usb_destroy is called.
+ */
+
+ ret = -ENODEV;
+ }
+ else
+ {
+ /* Ready for normal operation as a block device driver */
+
+ uvdbg("Successfully initialized\n");
+ priv->crefs--;
+ usbhost_givesem(&priv->exclsem);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_getle16
+ *
+ * Description:
+ * Get a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the little endian value.
+ *
+ * Returned Values:
+ * A uint16_t representing the whole 16-bit integer value
+ *
+ ****************************************************************************/
+
+static inline uint16_t usbhost_getle16(const uint8_t *val)
+{
+ return (uint16_t)val[1] << 8 | (uint16_t)val[0];
+}
+
+/****************************************************************************
+ * Name: usbhost_putle16
+ *
+ * Description:
+ * Put a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 16-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_putle16(uint8_t *dest, uint16_t val)
+{
+ dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
+ dest[1] = val >> 8;
+}
+
+/****************************************************************************
+ * Name: usbhost_getle32
+ *
+ * Description:
+ * Get a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the big endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline uint32_t usbhost_getle32(const uint8_t *val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ return (uint32_t)usbhost_getle16(&val[2]) << 16 | (uint32_t)usbhost_getle16(val);
+}
+
+/****************************************************************************
+ * Name: usbhost_putle32
+ *
+ * Description:
+ * Put a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_putle32(uint8_t *dest, uint32_t val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ usbhost_putle16(dest, (uint16_t)(val & 0xffff));
+ usbhost_putle16(dest+2, (uint16_t)(val >> 16));
+}
+
+/****************************************************************************
+ * Name: usbhost_talloc
+ *
+ * Description:
+ * Allocate transfer buffer memory.
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * On sucess, zero (OK) is returned. On failure, an negated errno value
+ * is returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int usbhost_talloc(FAR struct usbhost_state_s *priv)
+{
+ DEBUGASSERT(priv && priv->tbuffer == NULL);
+ return DRVR_ALLOC(priv->drvr, &priv->tbuffer, &priv->tbuflen);
+}
+
+/****************************************************************************
+ * Name: usbhost_tfree
+ *
+ * Description:
+ * Free transfer buffer memory.
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * On sucess, zero (OK) is returned. On failure, an negated errno value
+ * is returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int usbhost_tfree(FAR struct usbhost_state_s *priv)
+{
+ int result = OK;
+ DEBUGASSERT(priv);
+
+ if (priv->tbuffer)
+ {
+ DEBUGASSERT(priv->drvr);
+ result = DRVR_FREE(priv->drvr, priv->tbuffer);
+ priv->tbuffer = NULL;
+ priv->tbuflen = 0;
+ }
+ return result;
+}
+
+/****************************************************************************
+ * struct usbhost_registry_s methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_create
+ *
+ * Description:
+ * This function implements the create() method of struct usbhost_registry_s.
+ * The create() method is a callback into the class implementation. It is
+ * used to (1) create a new instance of the USB host class state and to (2)
+ * bind a USB host driver "session" to the class instance. Use of this
+ * create() method will support environments where there may be multiple
+ * USB ports and multiple USB devices simultaneously connected.
+ *
+ * Input Parameters:
+ * drvr - An instance of struct usbhost_driver_s that the class
+ * implementation will "bind" to its state structure and will
+ * subsequently use to communicate with the USB host driver.
+ * id - In the case where the device supports multiple base classes,
+ * subclasses, or protocols, this specifies which to configure for.
+ *
+ * Returned Values:
+ * On success, this function will return a non-NULL instance of struct
+ * usbhost_class_s that can be used by the USB host driver to communicate
+ * with the USB host class. NULL is returned on failure; this function
+ * will fail only if the drvr input parameter is NULL or if there are
+ * insufficient resources to create another USB host class instance.
+ *
+ ****************************************************************************/
+
+static FAR struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *drvr,
+ FAR const struct usbhost_id_s *id)
+{
+ FAR struct usbhost_state_s *priv;
+
+ /* Allocate a USB host class instance */
+
+ priv = usbhost_allocclass();
+ if (priv)
+ {
+ /* Initialize the allocated storage class instance */
+
+ memset(priv, 0, sizeof(struct usbhost_state_s));
+
+ /* Assign a device number to this class instance */
+
+ if (usbhost_allocdevno(priv) == OK)
+ {
+ /* Initialize class method function pointers */
+
+ priv->class.connect = usbhost_connect;
+ priv->class.disconnected = usbhost_disconnected;
+
+ /* The initial reference count is 1... One reference is held by the driver */
+
+ priv->crefs = 1;
+
+ /* Initialize semphores (this works okay in the interrupt context) */
+
+ sem_init(&priv->exclsem, 0, 1);
+
+ /* Bind the driver to the storage class instance */
+
+ priv->drvr = drvr;
+
+ /* Return the instance of the USB class driver */
+
+ return &priv->class;
+ }
+ }
+
+ /* An error occurred. Free the allocation and return NULL on all failures */
+
+ if (priv)
+ {
+ usbhost_freeclass(priv);
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ * struct usbhost_class_s methods
+ ****************************************************************************/
+/****************************************************************************
+ * Name: usbhost_connect
+ *
+ * Description:
+ * This function implements the connect() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to provide the device's configuration
+ * descriptor to the class so that the class may initialize properly
+ *
+ * Input Parameters:
+ * class - The USB host class entry previously obtained from a call to create().
+ * configdesc - A pointer to a uint8_t buffer container the configuration descripor.
+ * desclen - The length in bytes of the configuration descriptor.
+ * funcaddr - The USB address of the function containing the endpoint that EP0
+ * controls
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value is
+ * returned indicating the nature of the failure
+ *
+ * NOTE that the class instance remains valid upon return with a failure. It is
+ * the responsibility of the higher level enumeration logic to call
+ * CLASS_DISCONNECTED to free up the class driver resources.
+ *
+ * Assumptions:
+ * - This function will *not* be called from an interrupt handler.
+ * - If this function returns an error, the USB host controller driver
+ * must call to DISCONNECTED method to recover from the error
+ *
+ ****************************************************************************/
+
+static int usbhost_connect(FAR struct usbhost_class_s *class,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr)
+{
+ FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class;
+ int ret;
+
+ DEBUGASSERT(priv != NULL &&
+ configdesc != NULL &&
+ desclen >= sizeof(struct usb_cfgdesc_s));
+
+ /* Parse the configuration descriptor to get the endpoints */
+
+ ret = usbhost_cfgdesc(priv, configdesc, desclen, funcaddr);
+ if (ret != OK)
+ {
+ udbg("usbhost_cfgdesc() failed: %d\n", ret);
+ }
+ else
+ {
+ /* Now configure the device and register the NuttX driver */
+
+ ret = usbhost_devinit(priv);
+ if (ret != OK)
+ {
+ udbg("usbhost_devinit() failed: %d\n", ret);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_disconnected
+ *
+ * Description:
+ * This function implements the disconnected() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to inform the class that the USB device has
+ * been disconnected.
+ *
+ * Input Parameters:
+ * class - The USB host class entry previously obtained from a call to
+ * create().
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value
+ * is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * This function may be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int usbhost_disconnected(struct usbhost_class_s *class)
+{
+ FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class;
+ irqstate_t flags;
+
+ DEBUGASSERT(priv != NULL);
+
+ /* Set an indication to any users of the device that the device is no
+ * longer available.
+ */
+
+ flags = irqsave();
+ priv->disconnected = true;
+
+ /* Now check the number of references on the class instance. If it is one,
+ * then we can free the class instance now. Otherwise, we will have to
+ * wait until the holders of the references free them by closing the
+ * block driver.
+ */
+
+ ullvdbg("crefs: %d\n", priv->crefs);
+ if (priv->crefs == 1)
+ {
+ /* Destroy the class instance. If we are executing from an interrupt
+ * handler, then defer the destruction to the worker thread.
+ * Otherwise, destroy the instance now.
+ */
+
+ if (up_interrupt_context())
+ {
+ /* Destroy the instance on the worker thread. */
+
+ uvdbg("Queuing destruction: worker %p->%p\n", priv->work.worker, usbhost_destroy);
+ DEBUGASSERT(priv->work.worker == NULL);
+ (void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0);
+ }
+ else
+ {
+ /* Do the work now */
+
+ usbhost_destroy(priv);
+ }
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_skelinit
+ *
+ * Description:
+ * Initialize the USB class driver. This function should be called
+ * be platform-specific code in order to initialize and register support
+ * for the USB host class device.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Values:
+ * On success this function will return zero (OK); A negated errno value
+ * will be returned on failure.
+ *
+ ****************************************************************************/
+
+int usbhost_skelinit(void)
+{
+ /* Perform any one-time initialization of the class implementation */
+
+ /* Advertise our availability to support (certain) devices */
+
+ return usbhost_registerclass(&g_skeleton);
+}
diff --git a/nuttx/drivers/usbhost/usbhost_storage.c b/nuttx/drivers/usbhost/usbhost_storage.c
new file mode 100644
index 000000000..2b14676d7
--- /dev/null
+++ b/nuttx/drivers/usbhost/usbhost_storage.c
@@ -0,0 +1,2244 @@
+/****************************************************************************
+ * drivers/usbhost/usbhost_storage.c
+ *
+ * Copyright (C) 2010-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/scsi.h>
+
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbhost.h>
+#include <nuttx/usb/storage.h>
+
+/* Don't compile if prerequisites are not met */
+
+#if defined(CONFIG_USBHOST) && !defined(CONFIG_USBHOST_BULK_DISABLE) && \
+ !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+# warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
+#endif
+
+/* If the create() method is called by the USB host device driver from an
+ * interrupt handler, then it will be unable to call kmalloc() in order to
+ * allocate a new class instance. If the create() method is called from the
+ * interrupt level, then class instances must be pre-allocated.
+ */
+
+#ifndef CONFIG_USBHOST_NPREALLOC
+# define CONFIG_USBHOST_NPREALLOC 0
+#endif
+
+#if CONFIG_USBHOST_NPREALLOC > 26
+# error "Currently limited to 26 devices /dev/sda-z"
+#endif
+
+/* Driver support ***********************************************************/
+/* This format is used to construct the /dev/sd[n] device driver path. It
+ * defined here so that it will be used consistently in all places.
+ */
+
+#define DEV_FORMAT "/dev/sd%c"
+#define DEV_NAMELEN 10
+
+/* Used in usbhost_connect() */
+
+#define USBHOST_IFFOUND 0x01
+#define USBHOST_BINFOUND 0x02
+#define USBHOST_BOUTFOUND 0x04
+#define USBHOST_ALLFOUND 0x07
+
+#define USBHOST_MAX_RETRIES 100
+#define USBHOST_MAX_CREFS 0x7fff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure contains the internal, private state of the USB host mass
+ * storage class.
+ */
+
+struct usbhost_state_s
+{
+ /* This is the externally visible portion of the state */
+
+ struct usbhost_class_s class;
+
+ /* This is an instance of the USB host driver bound to this class instance */
+
+ struct usbhost_driver_s *drvr;
+
+ /* The remainder of the fields are provide to the mass storage class */
+
+ char sdchar; /* Character identifying the /dev/sd[n] device */
+ volatile bool disconnected; /* TRUE: Device has been disconnected */
+ uint8_t ifno; /* Interface number */
+ int16_t crefs; /* Reference count on the driver instance */
+ uint16_t blocksize; /* Block size of USB mass storage device */
+ uint32_t nblocks; /* Number of blocks on the USB mass storage device */
+ sem_t exclsem; /* Used to maintain mutual exclusive access */
+ struct work_s work; /* For interacting with the worker thread */
+ FAR uint8_t *tbuffer; /* The allocated transfer buffer */
+ size_t tbuflen; /* Size of the allocated transfer buffer */
+ usbhost_ep_t bulkin; /* Bulk IN endpoint */
+ usbhost_ep_t bulkout; /* Bulk OUT endpoint */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Semaphores */
+
+static void usbhost_takesem(sem_t *sem);
+#define usbhost_givesem(s) sem_post(s);
+
+/* Memory allocation services */
+
+static inline FAR struct usbhost_state_s *usbhost_allocclass(void);
+static inline void usbhost_freeclass(FAR struct usbhost_state_s *class);
+
+/* Device name management */
+
+static int usbhost_allocdevno(FAR struct usbhost_state_s *priv);
+static void usbhost_freedevno(FAR struct usbhost_state_s *priv);
+static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, char *devname);
+
+/* CBW/CSW debug helpers */
+
+#if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_VERBOSE)
+static void usbhost_dumpcbw(FAR struct usbmsc_cbw_s *cbw);
+static void usbhost_dumpcsw(FAR struct usbmsc_csw_s *csw);
+#else
+# define usbhost_dumpcbw(cbw);
+# define usbhost_dumpcsw(csw);
+#endif
+
+/* CBW helpers */
+
+static inline void usbhost_requestsensecbw(FAR struct usbmsc_cbw_s *cbw);
+static inline void usbhost_testunitreadycbw(FAR struct usbmsc_cbw_s *cbw);
+static inline void usbhost_readcapacitycbw(FAR struct usbmsc_cbw_s *cbw);
+static inline void usbhost_inquirycbw (FAR struct usbmsc_cbw_s *cbw);
+static inline void usbhost_readcbw (size_t startsector, uint16_t blocksize,
+ unsigned int nsectors,
+ FAR struct usbmsc_cbw_s *cbw);
+static inline void usbhost_writecbw(size_t startsector, uint16_t blocksize,
+ unsigned int nsectors,
+ FAR struct usbmsc_cbw_s *cbw);
+/* Command helpers */
+
+static inline int usbhost_maxlunreq(FAR struct usbhost_state_s *priv);
+static inline int usbhost_testunitready(FAR struct usbhost_state_s *priv);
+static inline int usbhost_requestsense(FAR struct usbhost_state_s *priv);
+static inline int usbhost_readcapacity(FAR struct usbhost_state_s *priv);
+static inline int usbhost_inquiry(FAR struct usbhost_state_s *priv);
+
+/* Worker thread actions */
+
+static void usbhost_destroy(FAR void *arg);
+
+/* Helpers for usbhost_connect() */
+
+static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr);
+static inline int usbhost_initvolume(FAR struct usbhost_state_s *priv);
+
+/* (Little Endian) Data helpers */
+
+static inline uint16_t usbhost_getle16(const uint8_t *val);
+static inline uint16_t usbhost_getbe16(const uint8_t *val);
+static inline void usbhost_putle16(uint8_t *dest, uint16_t val);
+static inline void usbhost_putbe16(uint8_t *dest, uint16_t val);
+static inline uint32_t usbhost_getle32(const uint8_t *val);
+static inline uint32_t usbhost_getbe32(const uint8_t *val);
+static void usbhost_putle32(uint8_t *dest, uint32_t val);
+static void usbhost_putbe32(uint8_t *dest, uint32_t val);
+
+/* Transfer descriptor memory management */
+
+static inline int usbhost_talloc(FAR struct usbhost_state_s *priv);
+static inline int usbhost_tfree(FAR struct usbhost_state_s *priv);
+static FAR struct usbmsc_cbw_s *usbhost_cbwalloc(FAR struct usbhost_state_s *priv);
+
+/* struct usbhost_registry_s methods */
+
+static struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *drvr,
+ FAR const struct usbhost_id_s *id);
+
+/* struct usbhost_class_s methods */
+
+static int usbhost_connect(FAR struct usbhost_class_s *class,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr);
+static int usbhost_disconnected(FAR struct usbhost_class_s *class);
+
+/* struct block_operations methods */
+
+static int usbhost_open(FAR struct inode *inode);
+static int usbhost_close(FAR struct inode *inode);
+static ssize_t usbhost_read(FAR struct inode *inode, FAR unsigned char *buffer,
+ size_t startsector, unsigned int nsectors);
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t usbhost_write(FAR struct inode *inode,
+ FAR const unsigned char *buffer, size_t startsector,
+ unsigned int nsectors);
+#endif
+static int usbhost_geometry(FAR struct inode *inode,
+ FAR struct geometry *geometry);
+static int usbhost_ioctl(FAR struct inode *inode, int cmd,
+ unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This structure provides the registry entry ID informatino that will be
+ * used to associate the USB host mass storage class to a connected USB
+ * device.
+ */
+
+static const const struct usbhost_id_s g_id =
+{
+ USB_CLASS_MASS_STORAGE, /* base */
+ USBMSC_SUBCLASS_SCSI, /* subclass */
+ USBMSC_PROTO_BULKONLY, /* proto */
+ 0, /* vid */
+ 0 /* pid */
+};
+
+/* This is the USB host storage class's registry entry */
+
+static struct usbhost_registry_s g_storage =
+{
+ NULL, /* flink */
+ usbhost_create, /* create */
+ 1, /* nids */
+ &g_id /* id[] */
+};
+
+/* Block driver operations. This is the interface exposed to NuttX by the
+ * class that permits it to behave like a block driver.
+ */
+
+static const struct block_operations g_bops =
+{
+ usbhost_open, /* open */
+ usbhost_close, /* close */
+ usbhost_read, /* read */
+#ifdef CONFIG_FS_WRITABLE
+ usbhost_write, /* write */
+#else
+ NULL, /* write */
+#endif
+ usbhost_geometry, /* geometry */
+ usbhost_ioctl /* ioctl */
+};
+
+/* This is an array of pre-allocated USB host storage class instances */
+
+#if CONFIG_USBHOST_NPREALLOC > 0
+static struct usbhost_state_s g_prealloc[CONFIG_USBHOST_NPREALLOC];
+#endif
+
+/* This is a list of free, pre-allocated USB host storage class instances */
+
+#if CONFIG_USBHOST_NPREALLOC > 0
+static struct usbhost_state_s *g_freelist;
+#endif
+
+/* This is a bitmap that is used to allocate device names /dev/sda-z. */
+
+static uint32_t g_devinuse;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_takesem
+ *
+ * Description:
+ * This is just a wrapper to handle the annoying behavior of semaphore
+ * waits that return due to the receipt of a signal.
+ *
+ ****************************************************************************/
+
+static void usbhost_takesem(sem_t *sem)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(sem) != 0)
+ {
+ /* The only case that an error should occr here is if the wait was
+ * awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: usbhost_allocclass
+ *
+ * Description:
+ * This is really part of the logic that implements the create() method
+ * of struct usbhost_registry_s. This function allocates memory for one
+ * new class instance.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Values:
+ * On success, this function will return a non-NULL instance of struct
+ * usbhost_class_s. NULL is returned on failure; this function will
+ * will fail only if there are insufficient resources to create another
+ * USB host class instance.
+ *
+ ****************************************************************************/
+
+#if CONFIG_USBHOST_NPREALLOC > 0
+static inline FAR struct usbhost_state_s *usbhost_allocclass(void)
+{
+ struct usbhost_state_s *priv;
+ irqstate_t flags;
+
+ /* We may be executing from an interrupt handler so we need to take one of
+ * our pre-allocated class instances from the free list.
+ */
+
+ flags = irqsave();
+ priv = g_freelist;
+ if (priv)
+ {
+ g_freelist = priv->class.flink;
+ priv->class.flink = NULL;
+ }
+ irqrestore(flags);
+ ullvdbg("Allocated: %p\n", priv);;
+ return priv;
+}
+#else
+static inline FAR struct usbhost_state_s *usbhost_allocclass(void)
+{
+ FAR struct usbhost_state_s *priv;
+
+ /* We are not executing from an interrupt handler so we can just call
+ * kmalloc() to get memory for the class instance.
+ */
+
+ DEBUGASSERT(!up_interrupt_context());
+ priv = (FAR struct usbhost_state_s *)kmalloc(sizeof(struct usbhost_state_s));
+ uvdbg("Allocated: %p\n", priv);;
+ return priv;
+}
+#endif
+
+/****************************************************************************
+ * Name: usbhost_freeclass
+ *
+ * Description:
+ * Free a class instance previously allocated by usbhost_allocclass().
+ *
+ * Input Parameters:
+ * class - A reference to the class instance to be freed.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+#if CONFIG_USBHOST_NPREALLOC > 0
+static inline void usbhost_freeclass(FAR struct usbhost_state_s *class)
+{
+ irqstate_t flags;
+ DEBUGASSERT(class != NULL);
+
+ ullvdbg("Freeing: %p\n", class);;
+
+ /* Just put the pre-allocated class structure back on the freelist */
+
+ flags = irqsave();
+ class->class.flink = g_freelist;
+ g_freelist = class;
+ irqrestore(flags);
+}
+#else
+static inline void usbhost_freeclass(FAR struct usbhost_state_s *class)
+{
+ DEBUGASSERT(class != NULL);
+
+ /* Free the class instance (calling sched_free() in case we are executing
+ * from an interrupt handler.
+ */
+
+ uvdbg("Freeing: %p\n", class);;
+ kfree(class);
+}
+#endif
+
+/****************************************************************************
+ * Name: Device name management
+ *
+ * Description:
+ * Some tiny functions to coordinate management of mass storage device names.
+ *
+ ****************************************************************************/
+
+static int usbhost_allocdevno(FAR struct usbhost_state_s *priv)
+{
+ irqstate_t flags;
+ int devno;
+
+ flags = irqsave();
+ for (devno = 0; devno < 26; devno++)
+ {
+ uint32_t bitno = 1 << devno;
+ if ((g_devinuse & bitno) == 0)
+ {
+ g_devinuse |= bitno;
+ priv->sdchar = 'a' + devno;
+ irqrestore(flags);
+ return OK;
+ }
+ }
+
+ irqrestore(flags);
+ return -EMFILE;
+}
+
+static void usbhost_freedevno(FAR struct usbhost_state_s *priv)
+{
+ int devno = 'a' - priv->sdchar;
+
+ if (devno >= 0 && devno < 26)
+ {
+ irqstate_t flags = irqsave();
+ g_devinuse &= ~(1 << devno);
+ irqrestore(flags);
+ }
+}
+
+static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, char *devname)
+{
+ (void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->sdchar);
+}
+
+/****************************************************************************
+ * Name: CBW/CSW debug helpers
+ *
+ * Description:
+ * The following functions are helper functions used to dump CBWs and CSWs.
+ *
+ * Input Parameters:
+ * cbw/csw - A reference to the CBW/CSW to dump.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_VERBOSE)
+static void usbhost_dumpcbw(FAR struct usbmsc_cbw_s *cbw)
+{
+ int i;
+
+ uvdbg("CBW:\n");
+ uvdbg(" signature: %08x\n", usbhost_getle32(cbw->signature));
+ uvdbg(" tag: %08x\n", usbhost_getle32(cbw->tag));
+ uvdbg(" datlen: %08x\n", usbhost_getle32(cbw->datlen));
+ uvdbg(" flags: %02x\n", cbw->flags);
+ uvdbg(" lun: %02x\n", cbw->lun);
+ uvdbg(" cdblen: %02x\n", cbw->cdblen);
+
+ uvdbg("CDB:\n");
+ for (i = 0; i < cbw->cdblen; i += 8)
+ {
+ uvdbg(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ cbw->cdb[i], cbw->cdb[i+1], cbw->cdb[i+2], cbw->cdb[i+3],
+ cbw->cdb[i+4], cbw->cdb[i+5], cbw->cdb[i+6], cbw->cdb[i+7]);
+ }
+}
+
+static void usbhost_dumpcsw(FAR struct usbmsc_csw_s *csw)
+{
+ uvdbg("CSW:\n");
+ uvdbg(" signature: %08x\n", usbhost_getle32(csw->signature));
+ uvdbg(" tag: %08x\n", usbhost_getle32(csw->tag));
+ uvdbg(" residue: %08x\n", usbhost_getle32(csw->residue));
+ uvdbg(" status: %02x\n", csw->status);
+}
+#endif
+
+/****************************************************************************
+ * Name: CBW helpers
+ *
+ * Description:
+ * The following functions are helper functions used to format CBWs.
+ *
+ * Input Parameters:
+ * cbw - A reference to allocated and initialized CBW to be built.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline void usbhost_requestsensecbw(FAR struct usbmsc_cbw_s *cbw)
+{
+ FAR struct scsicmd_requestsense_s *reqsense;
+
+ /* Format the CBW */
+
+ usbhost_putle32(cbw->datlen, SCSIRESP_FIXEDSENSEDATA_SIZEOF);
+ cbw->flags = USBMSC_CBWFLAG_IN;
+ cbw->cdblen = SCSICMD_REQUESTSENSE_SIZEOF;
+
+ /* Format the CDB */
+
+ reqsense = (FAR struct scsicmd_requestsense_s *)cbw->cdb;
+ reqsense->opcode = SCSI_CMD_REQUESTSENSE;
+ reqsense->alloclen = SCSIRESP_FIXEDSENSEDATA_SIZEOF;
+
+ usbhost_dumpcbw(cbw);
+}
+
+static inline void usbhost_testunitreadycbw(FAR struct usbmsc_cbw_s *cbw)
+{
+ /* Format the CBW */
+
+ cbw->cdblen = SCSICMD_TESTUNITREADY_SIZEOF;
+
+ /* Format the CDB */
+
+ cbw->cdb[0] = SCSI_CMD_TESTUNITREADY;
+
+ usbhost_dumpcbw(cbw);
+}
+
+static inline void usbhost_readcapacitycbw(FAR struct usbmsc_cbw_s *cbw)
+{
+ FAR struct scsicmd_readcapacity10_s *rcap10;
+
+ /* Format the CBW */
+
+ usbhost_putle32(cbw->datlen, SCSIRESP_READCAPACITY10_SIZEOF);
+ cbw->flags = USBMSC_CBWFLAG_IN;
+ cbw->cdblen = SCSICMD_READCAPACITY10_SIZEOF;
+
+ /* Format the CDB */
+
+ rcap10 = (FAR struct scsicmd_readcapacity10_s *)cbw->cdb;
+ rcap10->opcode = SCSI_CMD_READCAPACITY10;
+
+ usbhost_dumpcbw(cbw);
+}
+
+static inline void usbhost_inquirycbw (FAR struct usbmsc_cbw_s *cbw)
+{
+ FAR struct scscicmd_inquiry_s *inq;
+
+ /* Format the CBW */
+
+ usbhost_putle32(cbw->datlen, SCSIRESP_INQUIRY_SIZEOF);
+ cbw->flags = USBMSC_CBWFLAG_IN;
+ cbw->cdblen = SCSICMD_INQUIRY_SIZEOF;
+
+ /* Format the CDB */
+
+ inq = (FAR struct scscicmd_inquiry_s *)cbw->cdb;
+ inq->opcode = SCSI_CMD_INQUIRY;
+ usbhost_putbe16(inq->alloclen, SCSIRESP_INQUIRY_SIZEOF);
+
+ usbhost_dumpcbw(cbw);
+}
+
+static inline void
+usbhost_readcbw (size_t startsector, uint16_t blocksize,
+ unsigned int nsectors, FAR struct usbmsc_cbw_s *cbw)
+{
+ FAR struct scsicmd_read10_s *rd10;
+
+ /* Format the CBW */
+
+ usbhost_putle32(cbw->datlen, blocksize * nsectors);
+ cbw->flags = USBMSC_CBWFLAG_IN;
+ cbw->cdblen = SCSICMD_READ10_SIZEOF;
+
+ /* Format the CDB */
+
+ rd10 = (FAR struct scsicmd_read10_s *)cbw->cdb;
+ rd10->opcode = SCSI_CMD_READ10;
+ usbhost_putbe32(rd10->lba, startsector);
+ usbhost_putbe16(rd10->xfrlen, nsectors);
+
+ usbhost_dumpcbw(cbw);
+}
+
+static inline void
+usbhost_writecbw(size_t startsector, uint16_t blocksize,
+ unsigned int nsectors, FAR struct usbmsc_cbw_s *cbw)
+{
+ FAR struct scsicmd_write10_s *wr10;
+
+ /* Format the CBW */
+
+ usbhost_putle32(cbw->datlen, blocksize * nsectors);
+ cbw->cdblen = SCSICMD_WRITE10_SIZEOF;
+
+ /* Format the CDB */
+
+ wr10 = (FAR struct scsicmd_write10_s *)cbw->cdb;
+ wr10->opcode = SCSI_CMD_WRITE10;
+ usbhost_putbe32(wr10->lba, startsector);
+ usbhost_putbe16(wr10->xfrlen, nsectors);
+
+ usbhost_dumpcbw(cbw);
+}
+
+/****************************************************************************
+ * Name: Command helpers
+ *
+ * Description:
+ * The following functions are helper functions used to send commands.
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline int usbhost_maxlunreq(FAR struct usbhost_state_s *priv)
+{
+ FAR struct usb_ctrlreq_s *req = (FAR struct usb_ctrlreq_s *)priv->tbuffer;
+ DEBUGASSERT(priv && priv->tbuffer);
+ int ret;
+
+ /* Request maximum logical unit number. NOTE: On an IN transaction, The
+ * req and buffer pointers passed to DRVR_CTRLIN may refer to the same
+ * allocated memory.
+ */
+
+ uvdbg("Request maximum logical unit number\n");
+ memset(req, 0, sizeof(struct usb_ctrlreq_s));
+ req->type = USB_DIR_IN|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE;
+ req->req = USBMSC_REQ_GETMAXLUN;
+ usbhost_putle16(req->len, 1);
+
+ ret = DRVR_CTRLIN(priv->drvr, req, priv->tbuffer);
+ if (ret != OK)
+ {
+ /* Devices that do not support multiple LUNs may stall this command.
+ * On a failure, a single LUN is assumed.
+ */
+
+ *(priv->tbuffer) = 0;
+ }
+ return OK;
+}
+
+static inline int usbhost_testunitready(FAR struct usbhost_state_s *priv)
+{
+ FAR struct usbmsc_cbw_s *cbw;
+ int result;
+
+ /* Initialize a CBW (re-using the allocated transfer buffer) */
+
+ cbw = usbhost_cbwalloc(priv);
+ if (!cbw)
+ {
+ udbg("ERROR: Failed to create CBW\n");
+ return -ENOMEM;
+ }
+
+ /* Construct and send the CBW */
+
+ usbhost_testunitreadycbw(cbw);
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
+ (uint8_t*)cbw, USBMSC_CBW_SIZEOF);
+ if (result == OK)
+ {
+ /* Receive the CSW */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
+ priv->tbuffer, USBMSC_CSW_SIZEOF);
+ if (result == OK)
+ {
+ usbhost_dumpcsw((FAR struct usbmsc_csw_s *)priv->tbuffer);
+ }
+ }
+
+ return result;
+}
+
+static inline int usbhost_requestsense(FAR struct usbhost_state_s *priv)
+{
+ FAR struct usbmsc_cbw_s *cbw;
+ int result;
+
+ /* Initialize a CBW (re-using the allocated transfer buffer) */
+
+ cbw = usbhost_cbwalloc(priv);
+ if (!cbw)
+ {
+ udbg("ERROR: Failed to create CBW\n");
+ return -ENOMEM;
+ }
+
+ /* Construct and send the CBW */
+
+ usbhost_requestsensecbw(cbw);
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
+ (uint8_t*)cbw, USBMSC_CBW_SIZEOF);
+ if (result == OK)
+ {
+ /* Receive the sense data response */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
+ priv->tbuffer, SCSIRESP_FIXEDSENSEDATA_SIZEOF);
+ if (result == OK)
+ {
+ /* Receive the CSW */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
+ priv->tbuffer, USBMSC_CSW_SIZEOF);
+ if (result == OK)
+ {
+ usbhost_dumpcsw((FAR struct usbmsc_csw_s *)priv->tbuffer);
+ }
+ }
+ }
+
+ return result;
+}
+
+static inline int usbhost_readcapacity(FAR struct usbhost_state_s *priv)
+{
+ FAR struct usbmsc_cbw_s *cbw;
+ FAR struct scsiresp_readcapacity10_s *resp;
+ int result;
+
+ /* Initialize a CBW (re-using the allocated transfer buffer) */
+
+ cbw = usbhost_cbwalloc(priv);
+ if (!cbw)
+ {
+ udbg("ERROR: Failed to create CBW\n");
+ return -ENOMEM;
+ }
+
+ /* Construct and send the CBW */
+
+ usbhost_readcapacitycbw(cbw);
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
+ (uint8_t*)cbw, USBMSC_CBW_SIZEOF);
+ if (result == OK)
+ {
+ /* Receive the read capacity CBW IN response */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
+ priv->tbuffer, SCSIRESP_READCAPACITY10_SIZEOF);
+ if (result == OK)
+ {
+ /* Save the capacity information */
+
+ resp = (FAR struct scsiresp_readcapacity10_s *)priv->tbuffer;
+ priv->nblocks = usbhost_getbe32(resp->lba) + 1;
+ priv->blocksize = usbhost_getbe32(resp->blklen);
+
+ /* Receive the CSW */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
+ priv->tbuffer, USBMSC_CSW_SIZEOF);
+ if (result == OK)
+ {
+ usbhost_dumpcsw((FAR struct usbmsc_csw_s *)priv->tbuffer);
+ }
+ }
+ }
+
+ return result;
+}
+
+static inline int usbhost_inquiry(FAR struct usbhost_state_s *priv)
+{
+ FAR struct usbmsc_cbw_s *cbw;
+ FAR struct scsiresp_inquiry_s *resp;
+ int result;
+
+ /* Initialize a CBW (re-using the allocated transfer buffer) */
+
+ cbw = usbhost_cbwalloc(priv);
+ if (!cbw)
+ {
+ udbg("ERROR: Failed to create CBW\n");
+ return -ENOMEM;
+ }
+
+ /* Construct and send the CBW */
+
+ usbhost_inquirycbw(cbw);
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
+ (uint8_t*)cbw, USBMSC_CBW_SIZEOF);
+ if (result == OK)
+ {
+ /* Receive the CBW IN response */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
+ priv->tbuffer, SCSIRESP_INQUIRY_SIZEOF);
+ if (result == OK)
+ {
+ /* TODO: If USB debug is enabled, dump the response data here */
+
+ resp = (FAR struct scsiresp_inquiry_s *)priv->tbuffer;
+
+ /* Receive the CSW */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
+ priv->tbuffer, USBMSC_CSW_SIZEOF);
+ if (result == OK)
+ {
+ usbhost_dumpcsw((FAR struct usbmsc_csw_s *)priv->tbuffer);
+ }
+ }
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ * Name: usbhost_destroy
+ *
+ * Description:
+ * The USB mass storage device has been disconnected and the refernce count
+ * on the USB host class instance has gone to 1.. Time to destroy the USB
+ * host class instance.
+ *
+ * Input Parameters:
+ * arg - A reference to the class instance to be destroyed.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_destroy(FAR void *arg)
+{
+ FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg;
+ char devname[DEV_NAMELEN];
+
+ DEBUGASSERT(priv != NULL);
+ uvdbg("crefs: %d\n", priv->crefs);
+
+ /* Unregister the block driver */
+
+ usbhost_mkdevname(priv, devname);
+ (void)unregister_blockdriver(devname);
+
+ /* Release the device name used by this connection */
+
+ usbhost_freedevno(priv);
+
+ /* Free the bulk endpoints */
+
+ if (priv->bulkout)
+ {
+ DRVR_EPFREE(priv->drvr, priv->bulkout);
+ }
+
+ if (priv->bulkin)
+ {
+ DRVR_EPFREE(priv->drvr, priv->bulkin);
+ }
+
+ /* Free any transfer buffers */
+
+ usbhost_tfree(priv);
+
+ /* Destroy the semaphores */
+
+ sem_destroy(&priv->exclsem);
+
+ /* Disconnect the USB host device */
+
+ DRVR_DISCONNECT(priv->drvr);
+
+ /* And free the class instance. Hmmm.. this may execute on the worker
+ * thread and the work structure is part of what is getting freed. That
+ * should be okay because once the work contained is removed from the
+ * queue, it should not longer be accessed by the worker thread.
+ */
+
+ usbhost_freeclass(priv);
+}
+
+/****************************************************************************
+ * Name: usbhost_cfgdesc
+ *
+ * Description:
+ * This function implements the connect() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to provide the device's configuration
+ * descriptor to the class so that the class may initialize properly
+ *
+ * Input Parameters:
+ * priv - The USB host class instance.
+ * configdesc - A pointer to a uint8_t buffer container the configuration descripor.
+ * desclen - The length in bytes of the configuration descriptor.
+ * funcaddr - The USB address of the function containing the endpoint that EP0
+ * controls
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value is
+ * returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * This function will *not* be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr)
+{
+ FAR struct usb_cfgdesc_s *cfgdesc;
+ FAR struct usb_desc_s *desc;
+ FAR struct usbhost_epdesc_s bindesc;
+ FAR struct usbhost_epdesc_s boutdesc;
+ int remaining;
+ uint8_t found = 0;
+ int ret;
+
+ DEBUGASSERT(priv != NULL &&
+ configdesc != NULL &&
+ desclen >= sizeof(struct usb_cfgdesc_s));
+
+ /* Verify that we were passed a configuration descriptor */
+
+ cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc;
+ if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
+ {
+ return -EINVAL;
+ }
+
+ /* Get the total length of the configuration descriptor (little endian).
+ * It might be a good check to get the number of interfaces here too.
+ */
+
+ remaining = (int)usbhost_getle16(cfgdesc->totallen);
+
+ /* Skip to the next entry descriptor */
+
+ configdesc += cfgdesc->len;
+ remaining -= cfgdesc->len;
+
+ /* Loop where there are more dscriptors to examine */
+
+ while (remaining >= sizeof(struct usb_desc_s))
+ {
+ /* What is the next descriptor? */
+
+ desc = (FAR struct usb_desc_s *)configdesc;
+ switch (desc->type)
+ {
+ /* Interface descriptor. We really should get the number of endpoints
+ * from this descriptor too.
+ */
+
+ case USB_DESC_TYPE_INTERFACE:
+ {
+ FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)configdesc;
+
+ uvdbg("Interface descriptor\n");
+ DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC);
+
+ /* Save the interface number and mark ONLY the interface found */
+
+ priv->ifno = ifdesc->ifno;
+ found = USBHOST_IFFOUND;
+ }
+ break;
+
+ /* Endpoint descriptor. We expect two bulk endpoints, an IN and an
+ * OUT.
+ */
+
+ case USB_DESC_TYPE_ENDPOINT:
+ {
+ FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)configdesc;
+
+ uvdbg("Endpoint descriptor\n");
+ DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC);
+
+ /* Check for a bulk endpoint. We only support the bulk-only
+ * protocol so I suppose anything else should really be an error.
+ */
+
+ if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_BULK)
+ {
+ /* Yes.. it is a bulk endpoint. IN or OUT? */
+
+ if (USB_ISEPOUT(epdesc->addr))
+ {
+ /* It is an OUT bulk endpoint. There should be only one
+ * bulk OUT endpoint.
+ */
+
+ if ((found & USBHOST_BOUTFOUND) != 0)
+ {
+ /* Oops.. more than one endpoint. We don't know
+ * what to do with this.
+ */
+
+ return -EINVAL;
+ }
+ found |= USBHOST_BOUTFOUND;
+
+ /* Save the bulk OUT endpoint information */
+
+ boutdesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
+ boutdesc.in = false;
+ boutdesc.funcaddr = funcaddr;
+ boutdesc.xfrtype = USB_EP_ATTR_XFER_BULK;
+ boutdesc.interval = epdesc->interval;
+ boutdesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
+ uvdbg("Bulk OUT EP addr:%d mxpacketsize:%d\n",
+ boutdesc.addr, boutdesc.mxpacketsize);
+ }
+ else
+ {
+ /* It is an IN bulk endpoint. There should be only one
+ * bulk IN endpoint.
+ */
+
+ if ((found & USBHOST_BINFOUND) != 0)
+ {
+ /* Oops.. more than one endpoint. We don't know
+ * what to do with this.
+ */
+
+ return -EINVAL;
+ }
+ found |= USBHOST_BINFOUND;
+
+ /* Save the bulk IN endpoint information */
+
+ bindesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
+ bindesc.in = 1;
+ bindesc.funcaddr = funcaddr;
+ bindesc.xfrtype = USB_EP_ATTR_XFER_BULK;
+ bindesc.interval = epdesc->interval;
+ bindesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
+ uvdbg("Bulk IN EP addr:%d mxpacketsize:%d\n",
+ bindesc.addr, bindesc.mxpacketsize);
+ }
+ }
+ }
+ break;
+
+ /* Other descriptors are just ignored for now */
+
+ default:
+ break;
+ }
+
+ /* If we found everything we need with this interface, then break out
+ * of the loop early.
+ */
+
+ if (found == USBHOST_ALLFOUND)
+ {
+ break;
+ }
+
+ /* Increment the address of the next descriptor */
+
+ configdesc += desc->len;
+ remaining -= desc->len;
+ }
+
+ /* Sanity checking... did we find all of things that we need? Hmmm.. I wonder..
+ * can we work read-only or write-only if only one bulk endpoint found?
+ */
+
+ if (found != USBHOST_ALLFOUND)
+ {
+ ulldbg("ERROR: Found IF:%s BIN:%s BOUT:%s\n",
+ (found & USBHOST_IFFOUND) != 0 ? "YES" : "NO",
+ (found & USBHOST_BINFOUND) != 0 ? "YES" : "NO",
+ (found & USBHOST_BOUTFOUND) != 0 ? "YES" : "NO");
+ return -EINVAL;
+ }
+
+ /* We are good... Allocate the endpoints */
+
+ ret = DRVR_EPALLOC(priv->drvr, &boutdesc, &priv->bulkout);
+ if (ret != OK)
+ {
+ udbg("ERROR: Failed to allocate Bulk OUT endpoint\n");
+ return ret;
+ }
+
+ ret = DRVR_EPALLOC(priv->drvr, &bindesc, &priv->bulkin);
+ if (ret != OK)
+ {
+ udbg("ERROR: Failed to allocate Bulk IN endpoint\n");
+ (void)DRVR_EPFREE(priv->drvr, priv->bulkout);
+ return ret;
+ }
+
+ ullvdbg("Endpoints allocated\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbhost_initvolume
+ *
+ * Description:
+ * The USB mass storage device has been successfully connected. This
+ * completes the initialization operations. It is first called after the
+ * configuration descriptor has been received.
+ *
+ * This function is called from the connect() method. This function always
+ * executes on the thread of the caller of connect().
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline int usbhost_initvolume(FAR struct usbhost_state_s *priv)
+{
+ FAR struct usbmsc_csw_s *csw;
+ unsigned int retries;
+ int ret = OK;
+
+ DEBUGASSERT(priv != NULL);
+
+ /* Set aside a transfer buffer for exclusive use by the mass storage driver */
+
+ ret = usbhost_talloc(priv);
+ if (ret != OK)
+ {
+ udbg("ERROR: Failed to allocate transfer buffer\n");
+ return ret;
+ }
+
+ /* Increment the reference count. This will prevent usbhost_destroy() from
+ * being called asynchronously if the device is removed.
+ */
+
+ priv->crefs++;
+ DEBUGASSERT(priv->crefs == 2);
+
+ /* Request the maximum logical unit number */
+
+ uvdbg("Get max LUN\n");
+ ret = usbhost_maxlunreq(priv);
+
+ for (retries = 0; retries < USBHOST_MAX_RETRIES /* && ret == OK */; retries++)
+ {
+ uvdbg("Test unit ready, retries=%d\n", retries);
+
+ /* Wait just a bit */
+
+ usleep(50*1000);
+
+ /* Send TESTUNITREADY to see if the unit is ready */
+
+ ret = usbhost_testunitready(priv);
+ if (ret == OK)
+ {
+ /* Is the unit is ready */
+
+ csw = (FAR struct usbmsc_csw_s *)priv->tbuffer;
+ if (csw->status == 0)
+ {
+ /* Yes... break out of the loop */
+
+ break;
+ }
+
+ /* No.. Request mode sense information. The REQUEST SENSE command
+ * is sent only "to clear interlocked unit attention conditions."
+ * The returned status is ignored here.
+ */
+
+ uvdbg("Request sense\n");
+ ret = usbhost_requestsense(priv);
+ }
+ }
+
+ /* Did the unit become ready? Did an error occur? Or did we time out? */
+
+ if (retries >= USBHOST_MAX_RETRIES)
+ {
+ udbg("ERROR: Timeout!\n");
+ ret = -ETIMEDOUT;
+ }
+
+ if (ret == OK)
+ {
+ /* Get the capacity of the volume */
+
+ uvdbg("Read capacity\n");
+ ret = usbhost_readcapacity(priv);
+ if (ret == OK)
+ {
+ /* Check the CSW for errors */
+
+ csw = (FAR struct usbmsc_csw_s *)priv->tbuffer;
+ if (csw->status != 0)
+ {
+ udbg("ERROR: CSW status error: %d\n", csw->status);
+ ret = -ENODEV;
+ }
+ }
+ }
+
+ /* Get information about the volume */
+
+ if (ret == OK)
+ {
+ /* Inquiry */
+
+ uvdbg("Inquiry\n");
+ ret = usbhost_inquiry(priv);
+ if (ret == OK)
+ {
+ /* Check the CSW for errors */
+
+ csw = (FAR struct usbmsc_csw_s *)priv->tbuffer;
+ if (csw->status != 0)
+ {
+ udbg("ERROR: CSW status error: %d\n", csw->status);
+ ret = -ENODEV;
+ }
+ }
+ }
+
+ /* Register the block driver */
+
+ if (ret == OK)
+ {
+ char devname[DEV_NAMELEN];
+
+ uvdbg("Register block driver\n");
+ usbhost_mkdevname(priv, devname);
+ ret = register_blockdriver(devname, &g_bops, 0, priv);
+ }
+
+ /* Check if we successfully initialized. We now have to be concerned
+ * about asynchronous modification of crefs because the block
+ * driver has been registerd.
+ */
+
+ if (ret == OK)
+ {
+ usbhost_takesem(&priv->exclsem);
+ DEBUGASSERT(priv->crefs >= 2);
+
+ /* Decrement the reference count */
+
+ priv->crefs--;
+
+ /* Handle a corner case where (1) open() has been called so the
+ * reference count was > 2, but the device has been disconnected.
+ * In this case, the class instance needs to persist until close()
+ * is called.
+ */
+
+ if (priv->crefs <= 1 && priv->disconnected)
+ {
+ /* The will cause the enumeration logic to disconnect
+ * the class driver.
+ */
+
+ ret = -ENODEV;
+ }
+
+ /* Release the semaphore... there is a race condition here.
+ * Decrementing the reference count and releasing the semaphore
+ * allows usbhost_destroy() to execute (on the worker thread);
+ * the class driver instance could get destoryed before we are
+ * ready to handle it!
+ */
+
+ usbhost_givesem(&priv->exclsem);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_getle16
+ *
+ * Description:
+ * Get a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the little endian value.
+ *
+ * Returned Values:
+ * A uint16_t representing the whole 16-bit integer value
+ *
+ ****************************************************************************/
+
+static inline uint16_t usbhost_getle16(const uint8_t *val)
+{
+ return (uint16_t)val[1] << 8 | (uint16_t)val[0];
+}
+
+/****************************************************************************
+ * Name: usbhost_getbe16
+ *
+ * Description:
+ * Get a (possibly unaligned) 16-bit big endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the big endian value.
+ *
+ * Returned Values:
+ * A uint16_t representing the whole 16-bit integer value
+ *
+ ****************************************************************************/
+
+static inline uint16_t usbhost_getbe16(const uint8_t *val)
+{
+ return (uint16_t)val[0] << 8 | (uint16_t)val[1];
+}
+
+/****************************************************************************
+ * Name: usbhost_putle16
+ *
+ * Description:
+ * Put a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 16-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_putle16(uint8_t *dest, uint16_t val)
+{
+ dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
+ dest[1] = val >> 8;
+}
+
+/****************************************************************************
+ * Name: usbhost_putbe16
+ *
+ * Description:
+ * Put a (possibly unaligned) 16-bit big endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the big endian value.
+ * val - The 16-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_putbe16(uint8_t *dest, uint16_t val)
+{
+ dest[0] = val >> 8; /* Big endian means MS byte first in byte stream */
+ dest[1] = val & 0xff;
+}
+
+/****************************************************************************
+ * Name: usbhost_getle32
+ *
+ * Description:
+ * Get a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the big endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline uint32_t usbhost_getle32(const uint8_t *val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ return (uint32_t)usbhost_getle16(&val[2]) << 16 | (uint32_t)usbhost_getle16(val);
+}
+
+/****************************************************************************
+ * Name: usbhost_getbe32
+ *
+ * Description:
+ * Get a (possibly unaligned) 32-bit big endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the big endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline uint32_t usbhost_getbe32(const uint8_t *val)
+{
+ /* Big endian means MS halfword first in byte stream */
+
+ return (uint32_t)usbhost_getbe16(val) << 16 | (uint32_t)usbhost_getbe16(&val[2]);
+}
+
+/****************************************************************************
+ * Name: usbhost_putle32
+ *
+ * Description:
+ * Put a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_putle32(uint8_t *dest, uint32_t val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ usbhost_putle16(dest, (uint16_t)(val & 0xffff));
+ usbhost_putle16(dest+2, (uint16_t)(val >> 16));
+}
+
+/****************************************************************************
+ * Name: usbhost_putbe32
+ *
+ * Description:
+ * Put a (possibly unaligned) 32-bit big endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the big endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_putbe32(uint8_t *dest, uint32_t val)
+{
+ /* Big endian means MS halfword first in byte stream */
+
+ usbhost_putbe16(dest, (uint16_t)(val >> 16));
+ usbhost_putbe16(dest+2, (uint16_t)(val & 0xffff));
+}
+
+/****************************************************************************
+ * Name: usbhost_talloc
+ *
+ * Description:
+ * Allocate transfer buffer memory.
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * On sucess, zero (OK) is returned. On failure, an negated errno value
+ * is returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int usbhost_talloc(FAR struct usbhost_state_s *priv)
+{
+ DEBUGASSERT(priv && priv->tbuffer == NULL);
+ return DRVR_ALLOC(priv->drvr, &priv->tbuffer, &priv->tbuflen);
+}
+
+/****************************************************************************
+ * Name: usbhost_tfree
+ *
+ * Description:
+ * Free transfer buffer memory.
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * On sucess, zero (OK) is returned. On failure, an negated errno value
+ * is returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int usbhost_tfree(FAR struct usbhost_state_s *priv)
+{
+ int result = OK;
+ DEBUGASSERT(priv);
+
+ if (priv->tbuffer)
+ {
+ DEBUGASSERT(priv->drvr);
+ result = DRVR_FREE(priv->drvr, priv->tbuffer);
+ priv->tbuffer = NULL;
+ priv->tbuflen = 0;
+ }
+ return result;
+}
+
+/****************************************************************************
+ * Name: usbhost_cbwalloc
+ *
+ * Description:
+ * Initialize a CBW (re-using the allocated transfer buffer). Upon
+ * successful return, the CBW is cleared and has the CBW signature in place.
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static FAR struct usbmsc_cbw_s *usbhost_cbwalloc(FAR struct usbhost_state_s *priv)
+{
+ FAR struct usbmsc_cbw_s *cbw = NULL;
+
+ DEBUGASSERT(priv->tbuffer && priv->tbuflen >= sizeof(struct usbmsc_cbw_s))
+
+ /* Intialize the CBW sructure */
+
+ cbw = (FAR struct usbmsc_cbw_s *)priv->tbuffer;
+ memset(cbw, 0, sizeof(struct usbmsc_cbw_s));
+ usbhost_putle32(cbw->signature, USBMSC_CBW_SIGNATURE);
+ return cbw;
+}
+
+/****************************************************************************
+ * struct usbhost_registry_s methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_create
+ *
+ * Description:
+ * This function implements the create() method of struct usbhost_registry_s.
+ * The create() method is a callback into the class implementation. It is
+ * used to (1) create a new instance of the USB host class state and to (2)
+ * bind a USB host driver "session" to the class instance. Use of this
+ * create() method will support environments where there may be multiple
+ * USB ports and multiple USB devices simultaneously connected.
+ *
+ * Input Parameters:
+ * drvr - An instance of struct usbhost_driver_s that the class
+ * implementation will "bind" to its state structure and will
+ * subsequently use to communicate with the USB host driver.
+ * id - In the case where the device supports multiple base classes,
+ * subclasses, or protocols, this specifies which to configure for.
+ *
+ * Returned Values:
+ * On success, this function will return a non-NULL instance of struct
+ * usbhost_class_s that can be used by the USB host driver to communicate
+ * with the USB host class. NULL is returned on failure; this function
+ * will fail only if the drvr input parameter is NULL or if there are
+ * insufficient resources to create another USB host class instance.
+ *
+ ****************************************************************************/
+
+static FAR struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *drvr,
+ FAR const struct usbhost_id_s *id)
+{
+ FAR struct usbhost_state_s *priv;
+
+ /* Allocate a USB host mass storage class instance */
+
+ priv = usbhost_allocclass();
+ if (priv)
+ {
+ /* Initialize the allocated storage class instance */
+
+ memset(priv, 0, sizeof(struct usbhost_state_s));
+
+ /* Assign a device number to this class instance */
+
+ if (usbhost_allocdevno(priv) == OK)
+ {
+ /* Initialize class method function pointers */
+
+ priv->class.connect = usbhost_connect;
+ priv->class.disconnected = usbhost_disconnected;
+
+ /* The initial reference count is 1... One reference is held by the driver */
+
+ priv->crefs = 1;
+
+ /* Initialize semphores (this works okay in the interrupt context) */
+
+ sem_init(&priv->exclsem, 0, 1);
+
+ /* Bind the driver to the storage class instance */
+
+ priv->drvr = drvr;
+
+ /* NOTE: We do not yet know the geometry of the USB mass storage device */
+
+ /* Return the instance of the USB mass storage class */
+
+ return &priv->class;
+ }
+ }
+
+ /* An error occurred. Free the allocation and return NULL on all failures */
+
+ if (priv)
+ {
+ usbhost_freeclass(priv);
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ * struct usbhost_class_s methods
+ ****************************************************************************/
+/****************************************************************************
+ * Name: usbhost_connect
+ *
+ * Description:
+ * This function implements the connect() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to provide the device's configuration
+ * descriptor to the class so that the class may initialize properly
+ *
+ * Input Parameters:
+ * class - The USB host class entry previously obtained from a call to create().
+ * configdesc - A pointer to a uint8_t buffer container the configuration descripor.
+ * desclen - The length in bytes of the configuration descriptor.
+ * funcaddr - The USB address of the function containing the endpoint that EP0
+ * controls
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value is
+ * returned indicating the nature of the failure
+ *
+ * NOTE that the class instance remains valid upon return with a failure. It is
+ * the responsibility of the higher level enumeration logic to call
+ * CLASS_DISCONNECTED to free up the class driver resources.
+ *
+ * Assumptions:
+ * - This function will *not* be called from an interrupt handler.
+ * - If this function returns an error, the USB host controller driver
+ * must call to DISCONNECTED method to recover from the error
+ *
+ ****************************************************************************/
+
+static int usbhost_connect(FAR struct usbhost_class_s *class,
+ FAR const uint8_t *configdesc, int desclen,
+ uint8_t funcaddr)
+{
+ FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class;
+ int ret;
+
+ DEBUGASSERT(priv != NULL &&
+ configdesc != NULL &&
+ desclen >= sizeof(struct usb_cfgdesc_s));
+
+ /* Parse the configuration descriptor to get the bulk I/O endpoints */
+
+ ret = usbhost_cfgdesc(priv, configdesc, desclen, funcaddr);
+ if (ret != OK)
+ {
+ udbg("usbhost_cfgdesc() failed: %d\n", ret);
+ }
+ else
+ {
+ /* Now configure the LUNs and register the block driver(s) */
+
+ ret = usbhost_initvolume(priv);
+ if (ret != OK)
+ {
+ udbg("usbhost_initvolume() failed: %d\n", ret);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_disconnected
+ *
+ * Description:
+ * This function implements the disconnected() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to inform the class that the USB device has
+ * been disconnected.
+ *
+ * Input Parameters:
+ * class - The USB host class entry previously obtained from a call to
+ * create().
+ *
+ * Returned Values:
+ * On success, zero (OK) is returned. On a failure, a negated errno value
+ * is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * This function may be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int usbhost_disconnected(struct usbhost_class_s *class)
+{
+ FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class;
+ irqstate_t flags;
+
+ DEBUGASSERT(priv != NULL);
+
+ /* Set an indication to any users of the mass storage device that the device
+ * is no longer available.
+ */
+
+ flags = irqsave();
+ priv->disconnected = true;
+
+ /* Now check the number of references on the class instance. If it is one,
+ * then we can free the class instance now. Otherwise, we will have to
+ * wait until the holders of the references free them by closing the
+ * block driver.
+ */
+
+ ullvdbg("crefs: %d\n", priv->crefs);
+ if (priv->crefs == 1)
+ {
+ /* Destroy the class instance. If we are executing from an interrupt
+ * handler, then defer the destruction to the worker thread.
+ * Otherwise, destroy the instance now.
+ */
+
+ if (up_interrupt_context())
+ {
+ /* Destroy the instance on the worker thread. */
+
+ uvdbg("Queuing destruction: worker %p->%p\n", priv->work.worker, usbhost_destroy);
+ DEBUGASSERT(priv->work.worker == NULL);
+ (void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0);
+ }
+ else
+ {
+ /* Do the work now */
+
+ usbhost_destroy(priv);
+ }
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * struct block_operations methods
+ ****************************************************************************/
+/****************************************************************************
+ * Name: usbhost_open
+ *
+ * Description: Open the block device
+ *
+ ****************************************************************************/
+
+static int usbhost_open(FAR struct inode *inode)
+{
+ FAR struct usbhost_state_s *priv;
+ irqstate_t flags;
+ int ret;
+
+ uvdbg("Entry\n");
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct usbhost_state_s *)inode->i_private;
+
+ /* Make sure that we have exclusive access to the private data structure */
+
+ DEBUGASSERT(priv->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS);
+ usbhost_takesem(&priv->exclsem);
+
+ /* Check if the mass storage device is still connected. We need to disable
+ * interrupts momentarily to assure that there are no asynchronous disconnect
+ * events.
+ */
+
+ flags = irqsave();
+ if (priv->disconnected)
+ {
+ /* No... the block driver is no longer bound to the class. That means that
+ * the USB storage device is no longer connected. Refuse any further
+ * attempts to open the driver.
+ */
+
+ ret = -ENODEV;
+ }
+ else
+ {
+ /* Otherwise, just increment the reference count on the driver */
+
+ priv->crefs++;
+ ret = OK;
+ }
+ irqrestore(flags);
+
+ usbhost_givesem(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_close
+ *
+ * Description: close the block device
+ *
+ ****************************************************************************/
+
+static int usbhost_close(FAR struct inode *inode)
+{
+ FAR struct usbhost_state_s *priv;
+ irqstate_t flags;
+
+ uvdbg("Entry\n");
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct usbhost_state_s *)inode->i_private;
+
+ /* Decrement the reference count on the block driver */
+
+ DEBUGASSERT(priv->crefs > 1);
+ usbhost_takesem(&priv->exclsem);
+ priv->crefs--;
+
+ /* Release the semaphore. The following operations when crefs == 1 are
+ * safe because we know that there is no outstanding open references to
+ * the block driver.
+ */
+
+ usbhost_givesem(&priv->exclsem);
+
+ /* We need to disable interrupts momentarily to assure that there are
+ * no asynchronous disconnect events.
+ */
+
+ flags = irqsave();
+
+ /* Check if the USB mass storage device is still connected. If the
+ * storage device is not connected and the reference count just
+ * decremented to one, then unregister the block driver and free
+ * the class instance.
+ */
+
+ if (priv->crefs <= 1 && priv->disconnected)
+ {
+ /* Destroy the class instance */
+
+ DEBUGASSERT(priv->crefs == 1);
+ usbhost_destroy(priv);
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: usbhost_read
+ *
+ * Description:
+ * Read the specified numer of sectors from the read-ahead buffer or from
+ * the physical device.
+ *
+ ****************************************************************************/
+
+static ssize_t usbhost_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t startsector, unsigned int nsectors)
+{
+ FAR struct usbhost_state_s *priv;
+ ssize_t ret = 0;
+ int result;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct usbhost_state_s *)inode->i_private;
+ uvdbg("startsector: %d nsectors: %d sectorsize: %d\n",
+ startsector, nsectors, priv->blocksize);
+
+ /* Check if the mass storage device is still connected */
+
+ if (priv->disconnected)
+ {
+ /* No... the block driver is no longer bound to the class. That means that
+ * the USB storage device is no longer connected. Refuse any attempt to read
+ * from the device.
+ */
+
+ ret = -ENODEV;
+ }
+ else if (nsectors > 0)
+ {
+ FAR struct usbmsc_cbw_s *cbw;
+
+ usbhost_takesem(&priv->exclsem);
+
+ /* Assume allocation failure */
+
+ ret = -ENOMEM;
+
+ /* Initialize a CBW (re-using the allocated transfer buffer) */
+
+ cbw = usbhost_cbwalloc(priv);
+ if (cbw)
+ {
+ /* Loop in the event that EAGAIN is returned (mean that the
+ * transaction was NAKed and we should try again.
+ */
+
+ do
+ {
+ /* Assume some device failure */
+
+ ret = -ENODEV;
+
+ /* Construct and send the CBW */
+
+ usbhost_readcbw(startsector, priv->blocksize, nsectors, cbw);
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
+ (uint8_t*)cbw, USBMSC_CBW_SIZEOF);
+ if (result == OK)
+ {
+ /* Receive the user data */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
+ buffer, priv->blocksize * nsectors);
+ if (result == OK)
+ {
+ /* Receive the CSW */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
+ priv->tbuffer, USBMSC_CSW_SIZEOF);
+ if (result == OK)
+ {
+ FAR struct usbmsc_csw_s *csw;
+
+ /* Check the CSW status */
+
+ csw = (FAR struct usbmsc_csw_s *)priv->tbuffer;
+ if (csw->status == 0)
+ {
+ ret = nsectors;
+ }
+ }
+ }
+ }
+ } while (result == -EAGAIN);
+ }
+
+ usbhost_givesem(&priv->exclsem);
+ }
+
+ /* On success, return the number of blocks read */
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_write
+ *
+ * Description:
+ * Write the specified number of sectors to the write buffer or to the
+ * physical device.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t usbhost_write(FAR struct inode *inode, const unsigned char *buffer,
+ size_t startsector, unsigned int nsectors)
+{
+ FAR struct usbhost_state_s *priv;
+ ssize_t ret;
+ int result;
+
+ uvdbg("sector: %d nsectors: %d sectorsize: %d\n");
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct usbhost_state_s *)inode->i_private;
+
+ /* Check if the mass storage device is still connected */
+
+ if (priv->disconnected)
+ {
+ /* No... the block driver is no longer bound to the class. That means that
+ * the USB storage device is no longer connected. Refuse any attempt to
+ * write to the device.
+ */
+
+ ret = -ENODEV;
+ }
+ else
+ {
+ FAR struct usbmsc_cbw_s *cbw;
+
+ usbhost_takesem(&priv->exclsem);
+
+ /* Assume allocation failure */
+
+ ret = -ENOMEM;
+
+ /* Initialize a CBW (re-using the allocated transfer buffer) */
+
+ cbw = usbhost_cbwalloc(priv);
+ if (cbw)
+ {
+ /* Assume some device failure */
+
+ ret = -ENODEV;
+
+ /* Construct and send the CBW */
+
+ usbhost_writecbw(startsector, priv->blocksize, nsectors, cbw);
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
+ (uint8_t*)cbw, USBMSC_CBW_SIZEOF);
+ if (result == OK)
+ {
+ /* Send the user data */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
+ (uint8_t*)buffer, priv->blocksize * nsectors);
+ if (result == OK)
+ {
+ /* Receive the CSW */
+
+ result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
+ priv->tbuffer, USBMSC_CSW_SIZEOF);
+ if (result == OK)
+ {
+ FAR struct usbmsc_csw_s *csw;
+
+ /* Check the CSW status */
+
+ csw = (FAR struct usbmsc_csw_s *)priv->tbuffer;
+ if (csw->status == 0)
+ {
+ ret = nsectors;
+ }
+ }
+ }
+ }
+ }
+
+ usbhost_givesem(&priv->exclsem);
+ }
+
+ /* On success, return the number of blocks written */
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: usbhost_geometry
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int usbhost_geometry(FAR struct inode *inode, struct geometry *geometry)
+{
+ FAR struct usbhost_state_s *priv;
+ int ret = -EINVAL;
+
+ uvdbg("Entry\n");
+ DEBUGASSERT(inode && inode->i_private);
+
+ /* Check if the mass storage device is still connected */
+
+ priv = (FAR struct usbhost_state_s *)inode->i_private;
+ if (priv->disconnected)
+ {
+ /* No... the block driver is no longer bound to the class. That means that
+ * the USB storage device is no longer connected. Refuse to return any
+ * geometry info.
+ */
+
+ ret = -ENODEV;
+ }
+ else if (geometry)
+ {
+ /* Return the geometry of the USB mass storage device */
+
+ usbhost_takesem(&priv->exclsem);
+
+ geometry->geo_available = true;
+ geometry->geo_mediachanged = false;
+#ifdef CONFIG_FS_WRITABLE
+ geometry->geo_writeenabled = true;
+#else
+ geometry->geo_writeenabled = false;
+#endif
+ geometry->geo_nsectors = priv->nblocks;
+ geometry->geo_sectorsize = priv->blocksize;
+ usbhost_givesem(&priv->exclsem);
+
+ uvdbg("nsectors: %ld sectorsize: %d\n",
+ (long)geometry->geo_nsectors, geometry->geo_sectorsize);
+
+ ret = OK;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_ioctl
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int usbhost_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
+{
+ FAR struct usbhost_state_s *priv;
+ int ret;
+
+ uvdbg("Entry\n");
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct usbhost_state_s *)inode->i_private;
+
+ /* Check if the mass storage device is still connected */
+
+ if (priv->disconnected)
+ {
+ /* No... the block driver is no longer bound to the class. That means that
+ * the USB storage device is no longer connected. Refuse to process any
+ * ioctl commands.
+ */
+
+ ret = -ENODEV;
+ }
+ else
+ {
+ /* Process the IOCTL by command */
+
+ usbhost_takesem(&priv->exclsem);
+ switch (cmd)
+ {
+ /* Add support for ioctl commands here */
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+ usbhost_givesem(&priv->exclsem);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_storageinit
+ *
+ * Description:
+ * Initialize the USB host storage class. This function should be called
+ * be platform-specific code in order to initialize and register support
+ * for the USB host storage class.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Values:
+ * On success this function will return zero (OK); A negated errno value
+ * will be returned on failure.
+ *
+ ****************************************************************************/
+
+int usbhost_storageinit(void)
+{
+ /* If we have been configured to use pre-allocated storage class instances,
+ * then place all of the pre-allocated USB host storage class instances
+ * into a free list.
+ */
+
+#if CONFIG_USBHOST_NPREALLOC > 0
+ int i;
+
+ g_freelist = NULL;
+ for (i = 0; i < CONFIG_USBHOST_NPREALLOC; i++)
+ {
+ struct usbhost_state_s *class = &g_prealloc[i];
+ class->class.flink = g_freelist;
+ g_freelist = class;
+ }
+#endif
+
+ /* Advertise our availability to support (certain) mass storage devices */
+
+ return usbhost_registerclass(&g_storage);
+}
+
+#endif /* CONFIG_USBHOST && !CONFIG_USBHOST_BULK_DISABLE && !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 */
diff --git a/nuttx/drivers/watchdog.c b/nuttx/drivers/watchdog.c
new file mode 100644
index 000000000..4f7357444
--- /dev/null
+++ b/nuttx/drivers/watchdog.c
@@ -0,0 +1,575 @@
+/****************************************************************************
+ * drivers/watchdog.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/irq.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/watchdog.h>
+
+#ifdef CONFIG_WATCHDOG
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Debug ********************************************************************/
+/* Non-standard debug that may be enabled just for testing the watchdog driver */
+
+#ifdef CONFIG_DEBUG_WATCHDOG
+# define wddbg dbg
+# define wdvdbg vdbg
+# define wdlldbg lldbg
+# define wdllvdbg llvdbg
+#else
+# define wddbg(x...)
+# define wdvdbg(x...)
+# define wdlldbg(x...)
+# define wdllvdbg(x...)
+#endif
+
+/****************************************************************************
+ * Private Type Definitions
+ ****************************************************************************/
+
+/* This structure describes the state of the upper half driver */
+
+struct watchdog_upperhalf_s
+{
+ uint8_t crefs; /* The number of times the device has been opened */
+ sem_t exclsem; /* Supports mutual exclusion */
+ FAR char *path; /* Registration path */
+
+ /* The contained lower-half driver */
+
+ FAR struct watchdog_lowerhalf_s *lower;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int wdog_open(FAR struct file *filep);
+static int wdog_close(FAR struct file *filep);
+static ssize_t wdog_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen);
+static ssize_t wdog_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen);
+static int wdog_ioctl(FAR struct file *filep, int cmd,
+ unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_wdogops =
+{
+ wdog_open, /* open */
+ wdog_close, /* close */
+ wdog_read, /* read */
+ wdog_write, /* write */
+ 0, /* seek */
+ wdog_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , 0 /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/************************************************************************************
+ * Name: wdog_open
+ *
+ * Description:
+ * This function is called whenever the watchdog timer device is opened.
+ *
+ ************************************************************************************/
+
+static int wdog_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct watchdog_upperhalf_s *upper = inode->i_private;
+ uint8_t tmp;
+ int ret;
+
+ wdvdbg("crefs: %d\n", upper->crefs);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0)
+ {
+ ret = -errno;
+ goto errout;
+ }
+
+ /* Increment the count of references to the device. If this the first
+ * time that the driver has been opened for this device, then initialize
+ * the device.
+ */
+
+ tmp = upper->crefs + 1;
+ if (tmp == 0)
+ {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ goto errout_with_sem;
+ }
+
+ /* Save the new open count */
+
+ upper->crefs = tmp;
+ ret = OK;
+
+errout_with_sem:
+ sem_post(&upper->exclsem);
+
+errout:
+ return ret;
+}
+
+/************************************************************************************
+ * Name: wdog_close
+ *
+ * Description:
+ * This function is called when the watchdog timer device is closed.
+ *
+ ************************************************************************************/
+
+static int wdog_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct watchdog_upperhalf_s *upper = inode->i_private;
+ int ret;
+
+ wdvdbg("crefs: %d\n", upper->crefs);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0)
+ {
+ ret = -errno;
+ goto errout;
+ }
+
+ /* Decrement the references to the driver. If the reference count will
+ * decrement to 0, then uninitialize the driver.
+ */
+
+ if (upper->crefs > 0)
+ {
+ upper->crefs--;
+ }
+
+ sem_post(&upper->exclsem);
+ ret = OK;
+
+errout:
+ return ret;
+}
+
+/************************************************************************************
+ * Name: wdog_read
+ *
+ * Description:
+ * A dummy read method. This is provided only to satsify the VFS layer.
+ *
+ ************************************************************************************/
+
+static ssize_t wdog_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ /* Return zero -- usually meaning end-of-file */
+
+ return 0;
+}
+
+/************************************************************************************
+ * Name: wdog_write
+ *
+ * Description:
+ * A dummy write method. This is provided only to satsify the VFS layer.
+ *
+ ************************************************************************************/
+
+static ssize_t wdog_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+{
+ return 0;
+}
+
+/************************************************************************************
+ * Name: wdog_ioctl
+ *
+ * Description:
+ * The standard ioctl method. This is where ALL of the watchdog timer work is
+ * done.
+ *
+ ************************************************************************************/
+
+static int wdog_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct watchdog_upperhalf_s *upper = inode->i_private;
+ FAR struct watchdog_lowerhalf_s *lower = upper->lower;
+ int ret;
+
+ wdvdbg("cmd: %d arg: %ld\n", cmd, arg);
+ DEBUGASSERT(upper && lower);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Handle built-in ioctl commands */
+
+ switch (cmd)
+ {
+ /* cmd: WDIOC_START
+ * Description: Start the watchdog timer
+ * Argument: Ignored
+ */
+
+ case WDIOC_START:
+ {
+ /* Start the watchdog timer, resetting the time to the current timeout */
+
+ DEBUGASSERT(lower->ops->start); /* Required */
+ ret = lower->ops->start(lower);
+ }
+ break;
+
+ /* cmd: WDIOC_STOP
+ * Description: Stop the watchdog timer
+ * Argument: Ignored
+ */
+
+ case WDIOC_STOP:
+ {
+ /* Stop the watchdog timer */
+
+ DEBUGASSERT(lower->ops->stop); /* Required */
+ ret = lower->ops->stop(lower);
+ }
+ break;
+
+ /* cmd: WDIOC_GETSTATUS
+ * Description: et the status of the watchdog timer.
+ * Argument: A writeable pointer to struct watchdog_status_s.
+ */
+
+ case WDIOC_GETSTATUS:
+ {
+ FAR struct watchdog_status_s *status;
+
+ /* Get the current watchdog timer status */
+
+ if (lower->ops->getstatus) /* Optional */
+ {
+ status = (FAR struct watchdog_status_s *)((uintptr_t)arg);
+ if (status)
+ {
+ ret = lower->ops->getstatus(lower, status);
+ }
+ else
+ {
+ ret = -EINVAL;
+ }
+ }
+ else
+ {
+ ret = -ENOSYS;
+ }
+ }
+ break;
+
+ /* cmd: WDIOC_SETTIMEOUT
+ * Description: Reset the watchdog timeout to this value
+ * Argument: A 32-bit timeout value in milliseconds.
+ */
+
+ case WDIOC_SETTIMEOUT:
+ {
+ /* Set a new timeout value (and reset the watchdog timer) */
+
+ if (lower->ops->settimeout) /* Optional */
+ {
+ ret = lower->ops->settimeout(lower, (uint32_t)arg);
+ }
+ else
+ {
+ ret = -ENOSYS;
+ }
+ }
+ break;
+
+ /* cmd: WDIOC_CAPTURE
+ * Description: Do not reset. Instead, called this handler.
+ * Argument: A pointer to struct watchdog_capture_s.
+ */
+
+ case WDIOC_CAPTURE:
+ {
+ FAR struct watchdog_capture_s *capture;
+
+ /* Don't reset on watchdog timer timeout; instead, call this user
+ * provider timeout handler. NOTE: Providing handler==NULL will
+ * restore the reset behavior.
+ */
+
+ if (lower->ops->capture) /* Optional */
+ {
+ capture = (FAR struct watchdog_capture_s *)((uintptr_t)arg);
+ if (capture)
+ {
+ capture->oldhandler =
+ lower->ops->capture(lower, capture->newhandler);
+ ret = OK;
+ }
+ else
+ {
+ ret = -EINVAL;
+ }
+ }
+ else
+ {
+ ret = -ENOSYS;
+ }
+ }
+ break;
+
+ /* cmd: WDIOC_KEEPALIVE
+ * Description: Reset the watchdog timer ("ping", "pet the dog")
+ * Argument: Argument: Ignored
+ */
+
+ case WDIOC_KEEPALIVE:
+ {
+ /* Reset the watchdog timer to the current timeout value, prevent any
+ * imminent watchdog timeouts. This is sometimes referred as
+ * "pinging" the watchdog timer or "petting the dog".
+ */
+
+ if (lower->ops->keepalive) /* Optional */
+ {
+ ret = lower->ops->keepalive(lower);
+ }
+ else
+ {
+ ret = -ENOSYS;
+ }
+ }
+ break;
+
+
+ /* Any unrecognized IOCTL commands might be platform-specific ioctl commands */
+
+ default:
+ {
+ wdvdbg("Forwarding unrecognized cmd: %d arg: %ld\n", cmd, arg);
+
+ /* An ioctl commands that are not recognized by the "upper-half"
+ * driver are forwarded to the lower half driver through this
+ * method.
+ */
+
+ if (lower->ops->ioctl) /* Optional */
+ {
+ ret = lower->ops->ioctl(lower, cmd, arg);
+ }
+ else
+ {
+ ret = -ENOSYS;
+ }
+ }
+ break;
+ }
+
+ sem_post(&upper->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: watchdog_register
+ *
+ * Description:
+ * This function binds an instance of a "lower half" watchdog driver with the
+ * "upper half" watchdog device and registers that device so that can be used
+ * by application code.
+ *
+ * When this function is called, the "lower half" driver should be in the
+ * disabled state (as if the stop() method had already been called).
+ *
+ * Input parameters:
+ * dev path - The full path to the driver to be registers in the NuttX
+ * pseudo-filesystem. The recommended convention is to name all watchdog
+ * drivers as "/dev/watchdog0", "/dev/watchdog1", etc. where the driver
+ * path differs only in the "minor" number at the end of the device name.
+ * lower - A pointer to an instance of lower half watchdog driver. This
+ * instance is bound to the watchdog driver and must persists as long as
+ * the driver persists.
+ *
+ * Returned Value:
+ * On success, a non-NULL handle is returned to the caller. In the event
+ * of any failure, a NULL value is returned.
+ *
+ ****************************************************************************/
+
+FAR void *watchdog_register(FAR const char *path,
+ FAR struct watchdog_lowerhalf_s *lower)
+{
+ FAR struct watchdog_upperhalf_s *upper;
+ int ret;
+
+ DEBUGASSERT(path && lower);
+ wdvdbg("Entry: path=%s\n", path);
+
+ /* Allocate the upper-half data structure */
+
+ upper = (FAR struct watchdog_upperhalf_s *)
+ kzalloc(sizeof(struct watchdog_upperhalf_s));
+ if (!upper)
+ {
+ wddbg("Upper half allocation failed\n");
+ goto errout;
+ }
+
+ /* Initialize the watchdog timer device structure (it was already zeroed
+ * by kzalloc()).
+ */
+
+ sem_init(&upper->exclsem, 0, 1);
+ upper->lower = lower;
+
+ /* Copy the registration path */
+
+ upper->path = strdup(path);
+ if (!upper->path)
+ {
+ wddbg("Path allocation failed\n");
+ goto errout_with_upper;
+ }
+
+ /* Register the watchdog timer device */
+
+ ret = register_driver(path, &g_wdogops, 0666, upper);
+ if (ret < 0)
+ {
+ wddbg("register_driver failed: %d\n", ret);
+ goto errout_with_path;
+ }
+
+ return (FAR void *)upper;
+
+errout_with_path:
+ kfree(upper->path);
+
+errout_with_upper:
+ sem_destroy(&upper->exclsem);
+ kfree(upper);
+
+errout:
+ return NULL;
+}
+
+/****************************************************************************
+ * Name: watchdog_unregister
+ *
+ * Description:
+ * This function can be called to disable and unregister the watchdog
+ * device driver.
+ *
+ * Input parameters:
+ * handle - This is the handle that was returned by watchdog_register()
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void watchdog_unregister(FAR void *handle)
+{
+ FAR struct watchdog_upperhalf_s *upper;
+ FAR struct watchdog_lowerhalf_s *lower;
+
+ /* Recover the pointer to the upper-half driver state */
+
+ upper = (FAR struct watchdog_upperhalf_s *)handle;
+ lower = upper->lower;
+ DEBUGASSERT(upper && lower);
+
+ wdvdbg("Unregistering: %s\n", upper->path);
+
+ /* Disable the watchdog timer */
+
+ DEBUGASSERT(lower->ops->stop); /* Required */
+ (void)lower->ops->stop(lower);
+
+ /* Unregister the watchdog timer device */
+
+ (void)unregister_driver(upper->path);
+
+ /* Then free all of the driver resources */
+
+ kfree(upper->path);
+ sem_destroy(&upper->exclsem);
+ kfree(upper);
+}
+
+#endif /* CONFIG_WATCHDOG */
diff --git a/nuttx/drivers/wireless/Kconfig b/nuttx/drivers/wireless/Kconfig
new file mode 100644
index 000000000..ae2bf3130
--- /dev/null
+++ b/nuttx/drivers/wireless/Kconfig
@@ -0,0 +1,4 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
diff --git a/nuttx/drivers/wireless/Make.defs b/nuttx/drivers/wireless/Make.defs
new file mode 100644
index 000000000..f47f7666a
--- /dev/null
+++ b/nuttx/drivers/wireless/Make.defs
@@ -0,0 +1,47 @@
+############################################################################
+# drivers/wireless/Make.defs
+#
+# Copyright (C) 2011 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+ifeq ($(CONFIG_WIRELESS),y)
+
+# Include wireless drivers
+
+CSRCS += cc1101.c ISM1_868MHzGFSK100kbps.c ISM2_905MHzGFSK250kbps.c
+
+# Include wireless build support
+
+DEPPATH += --dep-path wireless/cc1101
+VPATH += :wireless/cc1101
+CFLAGS += ${shell $(TOPDIR)/tools/incdir.sh $(INCDIROPT) "$(CC)" $(TOPDIR)/drivers/wireless/cc1101}
+endif
diff --git a/nuttx/drivers/wireless/cc1101/ISM1_868MHzGFSK100kbps.c b/nuttx/drivers/wireless/cc1101/ISM1_868MHzGFSK100kbps.c
new file mode 100644
index 000000000..5c4c58ab2
--- /dev/null
+++ b/nuttx/drivers/wireless/cc1101/ISM1_868MHzGFSK100kbps.c
@@ -0,0 +1,113 @@
+/****************************************************************************
+ * drivers/wireless/cc1101/ISM1_868MHzGFSK100kbps.c
+ *
+ * Copyright (C) 2011 Uros Platise. All rights reserved.
+ * Copyright (C) 2011 Ales Verbic. All rights reserved.
+ *
+ * Authors: Uros Platise <uros.platise@isotel.eu>
+ * Ales Verbic <ales.verbic@isotel.eu>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <nuttx/wireless/cc1101.h>
+
+/** Settings for 868 MHz, GFSK at 100kbps
+ *
+ * ISM Region 1 (Europe) only, Band 868–870 MHz
+ *
+ * Frequency ERP Duty Cycle Bandwidth Remarks
+ * 868 – 868.6 MHz +14 dBm < 1% No limits
+ * 868.7 – 869.2 MHz +14 dBm < 0.1% No limits
+ * 869.3 – 869.4 MHz +10 dBm No limits < 25 kHz Appropriate access protocol required
+ * 869.4 – 869.65 MHz +27 dBm < 10% < 25 kHz Channels may be combined to one high speed channel
+ * 869.7 -870 MHz +7 dBm No limits No limits
+ *
+ * Deviation = 46.142578
+ * Base frequency = 867.999985
+ * Carrier frequency = 867.999985
+ * Channel number = 0
+ * Carrier frequency = 867.999985
+ * Modulated = true
+ * Modulation format = GFSK
+ * Manchester enable = false
+ * Sync word qualifier mode = 30/32 sync word bits detected
+ * Preamble count = 4
+ * Channel spacing = 199.813843
+ * Carrier frequency = 867.999985
+ * Data rate = 99.9069
+ * RX filter BW = 210.937500
+ * Data format = Normal mode
+ * Length config = Fixed packet length mode. Length configured in PKTLEN register
+ * CRC enable = true
+ * Packet length = 62
+ * Device address = 00
+ * Address config = NO Address check, no broadcast
+ * CRC autoflush = true
+ * PA ramping = false
+ * TX power = 0
+ */
+const struct c1101_rfsettings_s cc1101_rfsettings_ISM1_868MHzGFSK100kbps = {
+ .FSCTRL1 = 0x08, // FSCTRL1 Frequency Synthesizer Control
+ .FSCTRL0 = 0x00, // FSCTRL0 Frequency Synthesizer Control
+
+ .FREQ2 = 0x20, // FREQ2 Frequency Control Word, High Byte
+ .FREQ1 = 0x25, // FREQ1 Frequency Control Word, Middle Byte
+ .FREQ0 = 0xED, // FREQ0 Frequency Control Word, Low Byte
+
+ .MDMCFG4 = 0x8B, // MDMCFG4 Modem Configuration
+ .MDMCFG3 = 0xE5, // MDMCFG3 Modem Configuration
+ .MDMCFG2 = 0x13, // MDMCFG2 Modem Configuration
+ .MDMCFG1 = 0x22, // MDMCFG1 Modem Configuration
+ .MDMCFG0 = 0xE5, // MDMCFG0 Modem Configuration
+
+ .DEVIATN = 0x46, // DEVIATN Modem Deviation Setting
+
+ .FOCCFG = 0x1D, // FOCCFG Frequency Offset Compensation Configuration
+
+ .BSCFG = 0x1C, // BSCFG Bit Synchronization Configuration
+
+ .AGCCTRL2= 0xC7, // AGCCTRL2 AGC Control
+ .AGCCTRL1= 0x00, // AGCCTRL1 AGC Control
+ .AGCCTRL0= 0xB2, // AGCCTRL0 AGC Control
+
+ .FREND1 = 0xB6, // FREND1 Front End RX Configuration
+ .FREND0 = 0x10, // FREND0 Front End TX Configuration
+
+ .FSCAL3 = 0xEA, // FSCAL3 Frequency Synthesizer Calibration
+ .FSCAL2 = 0x2A, // FSCAL2 Frequency Synthesizer Calibration
+ .FSCAL1 = 0x00, // FSCAL1 Frequency Synthesizer Calibration
+ .FSCAL0 = 0x1F, // FSCAL0 Frequency Synthesizer Calibration
+
+ .CHMIN = 0, // Fix at 9th channel: 869.80 MHz +- 100 kHz RF Bandwidth
+ .CHMAX = 9, // single channel
+
+ .PAMAX = 8, // 0 means power OFF, 8 represents PA[7]
+ .PA = {0x03, 0x0F, 0x1E, 0x27, 0x67, 0x50, 0x81, 0xC2}
+};
diff --git a/nuttx/drivers/wireless/cc1101/ISM2_905MHzGFSK250kbps.c b/nuttx/drivers/wireless/cc1101/ISM2_905MHzGFSK250kbps.c
new file mode 100644
index 000000000..e5655bed6
--- /dev/null
+++ b/nuttx/drivers/wireless/cc1101/ISM2_905MHzGFSK250kbps.c
@@ -0,0 +1,111 @@
+/****************************************************************************
+ * drivers/wireless/cc1101/ISM2_905MHzGFSK250kbps.c
+ *
+ * Copyright (C) 2011 Uros Platise. All rights reserved.
+ * Copyright (C) 2011 Ales Verbic. All rights reserved.
+ *
+ * Authors: Uros Platise <uros.platise@isotel.eu>
+ * Ales Verbic <ales.verbic@isotel.eu>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <nuttx/wireless/cc1101.h>
+
+/** Settings for 905 MHz, GFSK at 250kbps
+ *
+ * ISM Region 2 (America) only, Band 902–928 MHz
+ *
+ * Cordless phones 1 W
+ * Microwave ovens 750 W
+ * Industrial heaters 100 kW
+ * Military radar 1000 kW
+ *
+ * Deviation = 126.953125
+ * Base frequency = 901.999969
+ * Carrier frequency = 905.998993
+ * Channel number = 20
+ * Carrier frequency = 905.998993
+ * Modulated = true
+ * Modulation format = GFSK
+ * Manchester enable = false
+ * Sync word qualifier mode = 30/32 sync word bits detected
+ * Preamble count = 4
+ * Channel spacing = 199.951172
+ * Carrier frequency = 905.998993
+ * Data rate = 249.939
+ * RX filter BW = 541.666667
+ * Data format = Normal mode
+ * Length config = Variable packet length mode. Packet length configured by the first byte after sync word
+ * CRC enable = true
+ * Packet length = 61
+ * Device address = 0
+ * Address config = No address check
+ * CRC autoflush = false
+ * PA ramping = false
+ * TX power = 0
+ */
+const struct c1101_rfsettings_s cc1101_rfsettings_ISM2_905MHzGFSK250kbps = {
+ .FSCTRL1 = 0x0C, // FSCTRL1 Frequency Synthesizer Control
+ .FSCTRL0 = 0x00, // FSCTRL0 Frequency Synthesizer Control
+
+ .FREQ2 = 0x22, // FREQ2 Frequency Control Word, High Byte
+ .FREQ1 = 0xB1, // FREQ1 Frequency Control Word, Middle Byte
+ .FREQ0 = 0x3B, // FREQ0 Frequency Control Word, Low Byte
+
+ .MDMCFG4 = 0x2D, // MDMCFG4 Modem Configuration
+ .MDMCFG3 = 0x3B, // MDMCFG3 Modem Configuration
+ .MDMCFG2 = 0x13, // MDMCFG2 Modem Configuration
+ .MDMCFG1 = 0x22, // MDMCFG1 Modem Configuration
+ .MDMCFG0 = 0xF8, // MDMCFG0 Modem Configuration
+
+ .DEVIATN = 0x62, // DEVIATN Modem Deviation Setting
+
+ .FOCCFG = 0x1D, // FOCCFG Frequency Offset Compensation Configuration
+
+ .BSCFG = 0x1C, // BSCFG Bit Synchronization Configuration
+
+ .AGCCTRL2= 0xC7, // AGCCTRL2 AGC Control
+ .AGCCTRL1= 0x00, // AGCCTRL1 AGC Control
+ .AGCCTRL0= 0xB0, // AGCCTRL0 AGC Control
+
+ .FREND1 = 0xB6, // FREND1 Front End RX Configuration
+ .FREND0 = 0x10, // FREND0 Front End TX Configuration
+
+ .FSCAL3 = 0xEA, // FSCAL3 Frequency Synthesizer Calibration
+ .FSCAL2 = 0x2A, // FSCAL2 Frequency Synthesizer Calibration
+ .FSCAL1 = 0x00, // FSCAL1 Frequency Synthesizer Calibration
+ .FSCAL0 = 0x1F, // FSCAL0 Frequency Synthesizer Calibration
+
+ .CHMIN = 0, // VERIFY REGULATIONS!
+ .CHMAX = 0xFF,
+
+ .PAMAX = 8, // 0 means power OFF, 8 represents PA[7]
+ .PA = {0x03, 0x0E, 0x1E, 0x27, 0x39, 0x8E, 0xCD, 0xC0}
+};
diff --git a/nuttx/drivers/wireless/cc1101/Kconfig b/nuttx/drivers/wireless/cc1101/Kconfig
new file mode 100644
index 000000000..ae2bf3130
--- /dev/null
+++ b/nuttx/drivers/wireless/cc1101/Kconfig
@@ -0,0 +1,4 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
diff --git a/nuttx/drivers/wireless/cc1101/cc1101.c b/nuttx/drivers/wireless/cc1101/cc1101.c
new file mode 100644
index 000000000..45faaecd2
--- /dev/null
+++ b/nuttx/drivers/wireless/cc1101/cc1101.c
@@ -0,0 +1,812 @@
+/****************************************************************************
+ * drivers/wireless/cc1101/cc1101.c
+ *
+ * Copyright (C) 2011 Uros Platise. All rights reserved.
+ *
+ * Authors: Uros Platise <uros.platise@isotel.eu>
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/** \file
+ * \author Uros Platise
+ * \brief Chipcon CC1101 Device Driver
+ *
+ * Features:
+ * - Maximum data length: 61 bytes CC1101_PACKET_MAXDATALEN
+ * - Packet length includes two additional bytes: CC1101_PACKET_MAXTOTALLEN
+ * - Requires one GDO to trigger end-of-packets in RX and TX modes.
+ * - Variable packet length with data payload between 1..61 bytes
+ * (three bytes are reserved for packet length, and RSSI and LQI
+ * appended at the end of RXFIFO after each reception)
+ * - Support for General Digital Outputs with overload protection
+ * (single XOSC pin is allowed, otherwise error is returned)
+ * - Loadable RF settings, one for ISM Region 1 (Europe) and one for
+ * ISM Region 2 (Complete America)
+ *
+ * Todo:
+ * - Extend max packet length up to 255 bytes or rather infinite < 4096 bytes
+ * - Power up/down modes
+ * - Sequencing between states or add protection for correct termination of
+ * various different state (so that CC1101 does not block in case of improper use)
+ *
+ * \par RSSI and LQI value interpretation
+ *
+ * The LQI can be read from the LQI status register or it can be appended
+ * to the received packet in the RX FIFO. LQI is a metric of the current
+ * quality of the received signal. The LQI gives an estimate of how easily
+ * a received signal can be demodulated by accumulating the magnitude of
+ * the error between ideal constellations and the received signal over
+ * the 64 symbols immediately following the sync word. LQI is best used
+ * as a relative measurement of the link quality (a high value indicates
+ * a better link than what a low value does), since the value is dependent
+ * on the modulation format.
+ *
+ * To simplify: If the received modulation is FSK or GFSK, the receiver
+ * will measure the frequency of each "bit" and compare it with the
+ * expected frequency based on the channel frequency and the deviation
+ * and the measured frequency offset. If other modulations are used, the
+ * error of the modulated parameter (frequency for FSK/GFSK, phase for
+ * MSK, amplitude for ASK etc) will be measured against the expected
+ * ideal value
+ *
+ * RSSI (Received Signal Strength Indicator) is a signal strength
+ * indication. It does not care about the "quality" or "correctness" of
+ * the signal. LQI does not care about the actual signal strength, but
+ * the signal quality often is linked to signal strength. This is because
+ * a strong signal is likely to be less affected by noise and thus will
+ * be seen as "cleaner" or more "correct" by the receiver.
+ *
+ * There are four to five "extreme cases" that can be used to illustrate
+ * how RSSI and LQI work:
+ * 1. A weak signal in the presence of noise may give low RSSI and low LQI.
+ * 2. A weak signal in "total" absence of noise may give low RSSI and high LQI.
+ * 3. Strong noise (usually coming from an interferer) may give high RSSI and low LQI.
+ * 4. A strong signal without much noise may give high RSSI and high LQI.
+ * 5. A very strong signal that causes the receiver to saturate may give
+ * high RSSI and low LQI.
+ *
+ * Note that both RSSI and LQI are best used as relative measurements since
+ * the values are dependent on the modulation format.
+ **/
+
+#include <nuttx/config.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/wireless/cc1101.h>
+
+
+/****************************************************************************
+ * Declarations
+ ****************************************************************************/
+
+#define CC1101_SPIFREQ_BURST 6500000 /* Hz, no delay */
+#define CC1101_SPIFREQ_SINGLE 9000000 /* Hz, single access only - no delay */
+
+#define CC1101_MCSM0_VALUE 0x1C
+
+/****************************************************************************
+ * Chipcon CC1101 Internal Registers
+ ****************************************************************************/
+
+/* Configuration Registers */
+
+#define CC1101_IOCFG2 0x00 /* GDO2 output pin configuration */
+#define CC1101_IOCFG1 0x01 /* GDO1 output pin configuration */
+#define CC1101_IOCFG0 0x02 /* GDO0 output pin configuration */
+#define CC1101_FIFOTHR 0x03 /* RX FIFO and TX FIFO thresholds */
+#define CC1101_SYNC1 0x04 /* Sync word, high byte */
+#define CC1101_SYNC0 0x05 /* Sync word, low byte */
+#define CC1101_PKTLEN 0x06 /* Packet length */
+#define CC1101_PKTCTRL1 0x07 /* Packet automation control */
+#define CC1101_PKTCTRL0 0x08 /* Packet automation control */
+#define CC1101_ADDR 0x09 /* Device address */
+#define CC1101_CHANNR 0x0A /* Channel number */
+#define CC1101_FSCTRL1 0x0B /* Frequency synthesizer control */
+#define CC1101_FSCTRL0 0x0C /* Frequency synthesizer control */
+#define CC1101_FREQ2 0x0D /* Frequency control word, high byte */
+#define CC1101_FREQ1 0x0E /* Frequency control word, middle byte */
+#define CC1101_FREQ0 0x0F /* Frequency control word, low byte */
+#define CC1101_MDMCFG4 0x10 /* Modem configuration */
+#define CC1101_MDMCFG3 0x11 /* Modem configuration */
+#define CC1101_MDMCFG2 0x12 /* Modem configuration */
+#define CC1101_MDMCFG1 0x13 /* Modem configuration */
+#define CC1101_MDMCFG0 0x14 /* Modem configuration */
+#define CC1101_DEVIATN 0x15 /* Modem deviation setting */
+#define CC1101_MCSM2 0x16 /* Main Radio Cntrl State Machine config */
+#define CC1101_MCSM1 0x17 /* Main Radio Cntrl State Machine config */
+#define CC1101_MCSM0 0x18 /* Main Radio Cntrl State Machine config */
+#define CC1101_FOCCFG 0x19 /* Frequency Offset Compensation config */
+#define CC1101_BSCFG 0x1A /* Bit Synchronization configuration */
+#define CC1101_AGCCTRL2 0x1B /* AGC control */
+#define CC1101_AGCCTRL1 0x1C /* AGC control */
+#define CC1101_AGCCTRL0 0x1D /* AGC control */
+#define CC1101_WOREVT1 0x1E /* High byte Event 0 timeout */
+#define CC1101_WOREVT0 0x1F /* Low byte Event 0 timeout */
+#define CC1101_WORCTRL 0x20 /* Wake On Radio control */
+#define CC1101_FREND1 0x21 /* Front end RX configuration */
+#define CC1101_FREND0 0x22 /* Front end TX configuration */
+#define CC1101_FSCAL3 0x23 /* Frequency synthesizer calibration */
+#define CC1101_FSCAL2 0x24 /* Frequency synthesizer calibration */
+#define CC1101_FSCAL1 0x25 /* Frequency synthesizer calibration */
+#define CC1101_FSCAL0 0x26 /* Frequency synthesizer calibration */
+#define CC1101_RCCTRL1 0x27 /* RC oscillator configuration */
+#define CC1101_RCCTRL0 0x28 /* RC oscillator configuration */
+#define CC1101_FSTEST 0x29 /* Frequency synthesizer cal control */
+#define CC1101_PTEST 0x2A /* Production test */
+#define CC1101_AGCTEST 0x2B /* AGC test */
+#define CC1101_TEST2 0x2C /* Various test settings */
+#define CC1101_TEST1 0x2D /* Various test settings */
+#define CC1101_TEST0 0x2E /* Various test settings */
+
+/* Status registers */
+
+#define CC1101_PARTNUM (0x30 | 0xc0) /* Part number */
+#define CC1101_VERSION (0x31 | 0xc0) /* Current version number */
+#define CC1101_FREQEST (0x32 | 0xc0) /* Frequency offset estimate */
+#define CC1101_LQI (0x33 | 0xc0) /* Demodulator estimate for link quality */
+#define CC1101_RSSI (0x34 | 0xc0) /* Received signal strength indication */
+#define CC1101_MARCSTATE (0x35 | 0xc0) /* Control state machine state */
+#define CC1101_WORTIME1 (0x36 | 0xc0) /* High byte of WOR timer */
+#define CC1101_WORTIME0 (0x37 | 0xc0) /* Low byte of WOR timer */
+#define CC1101_PKTSTATUS (0x38 | 0xc0) /* Current GDOx status and packet status */
+#define CC1101_VCO_VC_DAC (0x39 | 0xc0) /* Current setting from PLL cal module */
+#define CC1101_TXBYTES (0x3A | 0xc0) /* Underflow and # of bytes in TXFIFO */
+#define CC1101_RXBYTES (0x3B | 0xc0) /* Overflow and # of bytes in RXFIFO */
+#define CC1101_RCCTRL1_STATUS (0x3C | 0xc0) /* Last RC oscilator calibration results */
+#define CC1101_RCCTRL0_STATUS (0x3D | 0xc0) /* Last RC oscilator calibration results */
+
+/* Multi byte memory locations */
+
+#define CC1101_PATABLE 0x3E
+#define CC1101_TXFIFO 0x3F
+#define CC1101_RXFIFO 0x3F
+
+/* Definitions for burst/single access to registers */
+
+#define CC1101_WRITE_BURST 0x40
+#define CC1101_READ_SINGLE 0x80
+#define CC1101_READ_BURST 0xC0
+
+/* Strobe commands */
+
+#define CC1101_SRES 0x30 /* Reset chip. */
+#define CC1101_SFSTXON 0x31 /* Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). */
+#define CC1101_SXOFF 0x32 /* Turn off crystal oscillator. */
+#define CC1101_SCAL 0x33 /* Calibrate frequency synthesizer and turn it off */
+#define CC1101_SRX 0x34 /* Enable RX. Perform calibration first if switching from IDLE and MCSM0.FS_AUTOCAL=1. */
+#define CC1101_STX 0x35 /* Enable TX. Perform calibration first if IDLE and MCSM0.FS_AUTOCAL=1. */
+ /* If switching from RX state and CCA is enabled then go directly to TX if channel is clear. */
+#define CC1101_SIDLE 0x36 /* Exit RX / TX, turn off frequency synthesizer and exit Wake-On-Radio mode if applicable. */
+#define CC1101_SAFC 0x37 /* Perform AFC adjustment of the frequency synthesizer */
+#define CC1101_SWOR 0x38 /* Start automatic RX polling sequence (Wake-on-Radio) */
+#define CC1101_SPWD 0x39 /* Enter power down mode when CSn goes high. */
+#define CC1101_SFRX 0x3A /* Flush the RX FIFO buffer. */
+#define CC1101_SFTX 0x3B /* Flush the TX FIFO buffer. */
+#define CC1101_SWORRST 0x3C /* Reset real time clock. */
+#define CC1101_SNOP 0x3D /* No operation. */
+
+/* Modem Control */
+
+#define CC1101_MCSM0_XOSC_FORCE_ON 0x01
+
+
+/*
+ * Chip Status Byte
+ */
+
+/* Bit fields in the chip status byte */
+
+#define CC1101_STATUS_CHIP_RDYn_BM 0x80
+#define CC1101_STATUS_STATE_BM 0x70
+#define CC1101_STATUS_FIFO_BYTES_AVAILABLE_BM 0x0F
+
+/* Chip states */
+
+#define CC1101_STATE_MASK 0x70
+#define CC1101_STATE_IDLE 0x00
+#define CC1101_STATE_RX 0x10
+#define CC1101_STATE_TX 0x20
+#define CC1101_STATE_FSTXON 0x30
+#define CC1101_STATE_CALIBRATE 0x40
+#define CC1101_STATE_SETTLING 0x50
+#define CC1101_STATE_RX_OVERFLOW 0x60
+#define CC1101_STATE_TX_UNDERFLOW 0x70
+
+/* Values of the MACRSTATE register */
+
+#define CC1101_MARCSTATE_SLEEP 0x00
+#define CC1101_MARCSTATE_IDLE 0x01
+#define CC1101_MARCSTATE_XOFF 0x02
+#define CC1101_MARCSTATE_VCOON_MC 0x03
+#define CC1101_MARCSTATE_REGON_MC 0x04
+#define CC1101_MARCSTATE_MANCAL 0x05
+#define CC1101_MARCSTATE_VCOON 0x06
+#define CC1101_MARCSTATE_REGON 0x07
+#define CC1101_MARCSTATE_STARTCAL 0x08
+#define CC1101_MARCSTATE_BWBOOST 0x09
+#define CC1101_MARCSTATE_FS_LOCK 0x0A
+#define CC1101_MARCSTATE_IFADCON 0x0B
+#define CC1101_MARCSTATE_ENDCAL 0x0C
+#define CC1101_MARCSTATE_RX 0x0D
+#define CC1101_MARCSTATE_RX_END 0x0E
+#define CC1101_MARCSTATE_RX_RST 0x0F
+#define CC1101_MARCSTATE_TXRX_SWITCH 0x10
+#define CC1101_MARCSTATE_RXFIFO_OVERFLOW 0x11
+#define CC1101_MARCSTATE_FSTXON 0x12
+#define CC1101_MARCSTATE_TX 0x13
+#define CC1101_MARCSTATE_TX_END 0x14
+#define CC1101_MARCSTATE_RXTX_SWITCH 0x15
+#define CC1101_MARCSTATE_TXFIFO_UNDERFLOW 0x16
+
+/* Part number and version */
+
+#define CC1101_PARTNUM_VALUE 0x00
+#define CC1101_VERSION_VALUE 0x04
+
+/*
+ * Others ...
+ */
+
+#define CC1101_LQI_CRC_OK_BM 0x80
+#define CC1101_LQI_EST_BM 0x7F
+
+
+/****************************************************************************
+ * Private Data Types
+ ****************************************************************************/
+
+#define FLAGS_RXONLY 1 /* Indicates receive operation only */
+#define FLAGS_XOSCENABLED 2 /* Indicates that one pin is configured as XOSC/n */
+
+struct cc1101_dev_s {
+ const struct c1101_rfsettings_s *rfsettings;
+
+ struct spi_dev_s * spi;
+ uint8_t isrpin; /* CC1101 pin used to trigger interrupts */
+ uint32_t pinset; /* GPIO of the MCU */
+ uint8_t flags;
+ uint8_t channel;
+ uint8_t power;
+};
+
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+void cc1101_access_begin(struct cc1101_dev_s * dev)
+{
+ (void)SPI_LOCK(dev->spi, true);
+ SPI_SELECT(dev->spi, SPIDEV_WIRELESS, true);
+ SPI_SETMODE(dev->spi, SPIDEV_MODE0); /* CPOL=0, CPHA=0 */
+ SPI_SETBITS(dev->spi, 8);
+}
+
+
+void cc1101_access_end(struct cc1101_dev_s * dev)
+{
+ SPI_SELECT(dev->spi, SPIDEV_WIRELESS, false);
+ (void)SPI_LOCK(dev->spi, false);
+}
+
+
+/** CC1101 Access with Range Check
+ *
+ * \param dev CC1101 Private Structure
+ * \param addr CC1101 Address
+ * \param buf Pointer to buffer, either for read or write access
+ * \param length when >0 it denotes read access, when <0 it denotes write
+ * access of -length. abs(length) greater of 1 implies burst mode,
+ * however
+ * \return OK on success or errno is set.
+ */
+int cc1101_access(struct cc1101_dev_s * dev, uint8_t addr, uint8_t *buf, int length)
+{
+ int stabyte;
+
+ /* Address cannot explicitly define READ command while length WRITE.
+ * Also access to these cells is only permitted as one byte, eventhough
+ * transfer is marked as BURST!
+ */
+
+ if ( (addr & CC1101_READ_SINGLE) && length != 1 )
+ return ERROR;
+
+ /* Prepare SPI */
+
+ cc1101_access_begin(dev);
+
+ if (length>1 || length < -1)
+ SPI_SETFREQUENCY(dev->spi, CC1101_SPIFREQ_BURST);
+ else SPI_SETFREQUENCY(dev->spi, CC1101_SPIFREQ_SINGLE);
+
+ /* Transfer */
+
+ if (length <= 0) { /* 0 length are command strobes */
+ if (length < -1)
+ addr |= CC1101_WRITE_BURST;
+
+ stabyte = SPI_SEND(dev->spi, addr);
+ if (length) {
+ SPI_SNDBLOCK(dev->spi, buf, -length);
+ }
+ }
+ else {
+ addr |= CC1101_READ_SINGLE;
+ if (length > 1)
+ addr |= CC1101_READ_BURST;
+
+ stabyte = SPI_SEND(dev->spi, addr);
+ SPI_RECVBLOCK(dev->spi, buf, length);
+ }
+
+ cc1101_access_end(dev);
+
+ return stabyte;
+}
+
+
+/** Strobes command and returns chip status byte
+ *
+ * By default commands are send as Write. To a command,
+ * CC1101_READ_SINGLE may be OR'ed to obtain the number of RX bytes
+ * pending in RX FIFO.
+ */
+inline uint8_t cc1101_strobe(struct cc1101_dev_s * dev, uint8_t command)
+{
+ uint8_t status;
+
+ cc1101_access_begin(dev);
+ SPI_SETFREQUENCY(dev->spi, CC1101_SPIFREQ_SINGLE);
+
+ status = SPI_SEND(dev->spi, command);
+
+ cc1101_access_end(dev);
+
+ return status;
+}
+
+
+int cc1101_reset(struct cc1101_dev_s * dev)
+{
+ cc1101_strobe(dev, CC1101_SRES);
+ return OK;
+}
+
+
+int cc1101_checkpart(struct cc1101_dev_s * dev)
+{
+ uint8_t partnum, version;
+
+ if (cc1101_access(dev, CC1101_PARTNUM, &partnum, 1) < 0 ||
+ cc1101_access(dev, CC1101_VERSION, &version, 1) < 0)
+ return ERROR;
+
+ if (partnum == CC1101_PARTNUM_VALUE && version == CC1101_VERSION_VALUE)
+ return OK;
+
+ return ERROR;
+}
+
+
+void cc1101_dumpregs(struct cc1101_dev_s * dev, uint8_t addr, uint8_t length)
+{
+ uint8_t buf[0x30], i;
+
+ cc1101_access(dev, addr, buf, length);
+
+ printf("CC1101[%2x]: ", addr);
+ for (i=0; i<length; i++) printf(" %2x,", buf[i]);
+ printf("\n");
+}
+
+
+void cc1101_setpacketctrl(struct cc1101_dev_s * dev)
+{
+ uint8_t values[3];
+
+ values[0] = 0; /* Rx FIFO threshold = 32, Tx FIFO threshold = 33 */
+ cc1101_access(dev, CC1101_FIFOTHR, values, -1);
+
+ /* Packet length
+ * Limit it to 61 bytes in total: pktlen, data[61], rssi, lqi
+ */
+
+ values[0] = CC1101_PACKET_MAXDATALEN;
+ cc1101_access(dev, CC1101_PKTLEN, values, -1);
+
+ /* Packet Control */
+
+ values[0] = 0x04; /* Append status: RSSI and LQI at the end of received packet */
+ /* TODO: CRC Auto Flash bit 0x08 ??? */
+ values[1] = 0x05; /* CRC in Rx and Tx Enabled: Variable Packet mode, defined by first byte */
+ /* TODO: Enable data whitening ... */
+ cc1101_access(dev, CC1101_PKTCTRL1, values, -2);
+
+ /* Main Radio Control State Machine */
+
+ values[0] = 0x07; /* No time-out */
+ values[1] = 0x00; /* Clear channel if RSSI < thr && !receiving;
+ * TX -> RX, RX -> RX: 0x3F */
+ values[2] = CC1101_MCSM0_VALUE; /* Calibrate on IDLE -> RX/TX, OSC Timeout = ~500 us
+ TODO: has XOSC_FORCE_ON */
+ cc1101_access(dev, CC1101_MCSM2, values, -3);
+
+ /* Wake-On Radio Control */
+
+ // Not used yet.
+
+ // WOREVT1:WOREVT0 - 16-bit timeout register
+}
+
+
+/****************************************************************************
+ * Callbacks
+ ****************************************************************************/
+
+volatile int cc1101_interrupt = 0;
+
+/** External line triggers this callback
+ *
+ * The concept todo is:
+ * - GPIO provides EXTI Interrupt
+ * - It should handle EXTI Interrupts in ISR, to which chipcon can
+ * register a callback (and others). The ISR then foreach() calls a
+ * its callback, and it is up to peripheral to find, whether the cause
+ * of EXTI ISR was itself.
+ **/
+
+int cc1101_eventcb(int irq, FAR void *context)
+{
+ cc1101_interrupt++;
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+struct cc1101_dev_s * cc1101_init(struct spi_dev_s * spi, uint8_t isrpin,
+ uint32_t pinset, const struct c1101_rfsettings_s * rfsettings)
+{
+ struct cc1101_dev_s * dev;
+
+ ASSERT(spi);
+
+ if ( (dev = kmalloc( sizeof(struct cc1101_dev_s) )) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ dev->rfsettings = rfsettings;
+ dev->spi = spi;
+ dev->isrpin = isrpin;
+ dev->pinset = pinset;
+ dev->flags = 0;
+ dev->channel = rfsettings->CHMIN;
+ dev->power = rfsettings->PAMAX;
+
+ /* Reset chip, check status bytes */
+
+ if ( cc1101_reset(dev) < 0 ) {
+ kfree(dev);
+ errno = EFAULT;
+ return NULL;
+ }
+
+ /* Check part compatibility */
+
+ if ( cc1101_checkpart(dev) < 0 ) {
+ kfree(dev);
+ errno = ENODEV;
+ return NULL;
+ }
+
+ /* Configure CC1101:
+ * - disable GDOx for best performance
+ * - load RF
+ * - and packet control
+ */
+
+ cc1101_setgdo(dev, CC1101_PIN_GDO0, CC1101_GDO_HIZ);
+ cc1101_setgdo(dev, CC1101_PIN_GDO1, CC1101_GDO_HIZ);
+ cc1101_setgdo(dev, CC1101_PIN_GDO2, CC1101_GDO_HIZ);
+ cc1101_setrf(dev, rfsettings);
+ cc1101_setpacketctrl(dev);
+
+ /* Set the ISR to be triggerred on falling edge of the:
+ *
+ * 6 (0x06) Asserts when sync word has been sent / received, and
+ * de-asserts at the end of the packet. In RX, the pin will de-assert
+ * when the optional address check fails or the RX FIFO overflows.
+ * In TX the pin will de-assert if the TX FIFO underflows.
+ */
+
+ cc1101_setgdo(dev, dev->isrpin, CC1101_GDO_SYNC);
+
+ /* Bind to external interrupt line */
+
+ // depends on STM32: TODO: Make that config within pinset and
+ // provide general gpio interface
+ //stm32_gpiosetevent(pinset, false, true, true, cc1101_eventcb);
+
+ return dev;
+}
+
+
+int cc1101_deinit(struct cc1101_dev_s * dev)
+{
+ ASSERT(dev);
+
+ /* Release interrupt */
+ //stm32_gpiosetevent(pinset, false, false, false, NULL);
+
+ /* Power down chip */
+ cc1101_powerdown(dev);
+
+ /* Release external interrupt line */
+ kfree(dev);
+
+ return 0;
+}
+
+
+int cc1101_powerup(struct cc1101_dev_s * dev)
+{
+ ASSERT(dev);
+ return 0;
+}
+
+
+int cc1101_powerdown(struct cc1101_dev_s * dev)
+{
+ ASSERT(dev);
+ return 0;
+}
+
+
+int cc1101_setgdo(struct cc1101_dev_s * dev, uint8_t pin, uint8_t function)
+{
+ ASSERT(dev);
+ ASSERT(pin <= CC1101_IOCFG0);
+
+ if (function >= CC1101_GDO_CLK_XOSC1) {
+
+ /* Only one pin can be enabled at a time as XOSC/n */
+
+ if (dev->flags & FLAGS_XOSCENABLED) return -EPERM;
+
+ /* Force XOSC to stay active even in sleep mode */
+
+ int value = CC1101_MCSM0_VALUE | CC1101_MCSM0_XOSC_FORCE_ON;
+ cc1101_access(dev, CC1101_MCSM0, &value, -1);
+
+ dev->flags |= FLAGS_XOSCENABLED;
+ }
+ else if (dev->flags & FLAGS_XOSCENABLED) {
+
+ /* Disable XOSC in sleep mode */
+
+ int value = CC1101_MCSM0_VALUE;
+ cc1101_access(dev, CC1101_MCSM0, &value, -1);
+
+ dev->flags &= ~FLAGS_XOSCENABLED;
+ }
+
+ return cc1101_access(dev, pin, &function, -1);
+}
+
+
+int cc1101_setrf(struct cc1101_dev_s * dev, const struct c1101_rfsettings_s *settings)
+{
+ ASSERT(dev);
+ ASSERT(settings);
+
+ if (cc1101_access(dev, CC1101_FSCTRL1, &settings->FSCTRL1, -11) < 0) return ERROR;
+ if (cc1101_access(dev, CC1101_FOCCFG, &settings->FOCCFG, -5) < 0) return ERROR;
+ if (cc1101_access(dev, CC1101_FREND1, &settings->FREND1, -6) < 0) return ERROR;
+
+ /* Load Power Table */
+
+ if (cc1101_access(dev, CC1101_PATABLE, settings->PA, -8) < 0) return ERROR;
+
+ /* If channel is out of valid range, mark that. Limit power.
+ * We are not allowed to send any data, but are allowed to listen
+ * and receive.
+ */
+
+ cc1101_setchannel(dev, dev->channel);
+ cc1101_setpower(dev, dev->power);
+
+ return OK;
+}
+
+
+int cc1101_setchannel(struct cc1101_dev_s * dev, uint8_t channel)
+{
+ ASSERT(dev);
+
+ /* Store localy in further checks */
+
+ dev->channel = channel;
+
+ /* If channel is out of valid, we are allowed to listen and receive only */
+
+ if (channel < dev->rfsettings->CHMIN || channel > dev->rfsettings->CHMAX)
+ dev->flags |= FLAGS_RXONLY;
+ else dev->flags &= ~FLAGS_RXONLY;
+
+ cc1101_access(dev, CC1101_CHANNR, &dev->channel, -1);
+
+ return dev->flags & FLAGS_RXONLY;
+}
+
+
+uint8_t cc1101_setpower(struct cc1101_dev_s * dev, uint8_t power)
+{
+ ASSERT(dev);
+
+ if (power > dev->rfsettings->PAMAX)
+ power = dev->rfsettings->PAMAX;
+
+ dev->power = power;
+
+ if (power == 0) {
+ dev->flags |= FLAGS_RXONLY;
+ return 0;
+ }
+ else dev->flags &= ~FLAGS_RXONLY;
+
+ /* Add remaining part from RF table (to get rid of readback) */
+
+ power--;
+ power |= dev->rfsettings->FREND0;
+
+ /* On error, report that as zero power */
+
+ if (cc1101_access(dev, CC1101_FREND0, &power, -1) < 0)
+ dev->power = 0;
+
+ return dev->power;
+}
+
+
+int cc1101_calcRSSIdBm(int rssi)
+{
+ if (rssi >= 128) rssi -= 256;
+ return (rssi >> 1) - 74;
+}
+
+
+int cc1101_receive(struct cc1101_dev_s * dev)
+{
+ ASSERT(dev);
+
+ /* \todo Wait for IDLE before going into another state? */
+
+ cc1101_interrupt = 0;
+
+ cc1101_strobe(dev, CC1101_SRX | CC1101_READ_SINGLE);
+
+ return 0;
+}
+
+
+int cc1101_read(struct cc1101_dev_s * dev, uint8_t * buf, size_t size)
+{
+ ASSERT(dev);
+
+ if (buf==NULL) {
+ if (size==0) return 64;
+ // else received packet size
+ return 0;
+ }
+
+ if (cc1101_interrupt == 0) return 0;
+
+ int status = cc1101_strobe(dev, CC1101_SNOP | CC1101_READ_SINGLE);
+
+ if (status & CC1101_STATUS_FIFO_BYTES_AVAILABLE_BM &&
+ (status & CC1101_STATE_MASK) == CC1101_STATE_IDLE) {
+
+ uint8_t nbytes;
+
+ cc1101_access(dev, CC1101_RXFIFO, &nbytes, 1);
+
+ nbytes += 2; /* RSSI and LQI */
+
+ cc1101_access(dev, CC1101_RXFIFO, buf, (nbytes > size) ? size : nbytes);
+
+ /* Flush remaining bytes, if there is no room to receive
+ * or if there is a BAD CRC
+ */
+
+ if (nbytes > size || (nbytes <= size && !(buf[nbytes-1]&0x80)) ) {
+ printf("Flushing RX FIFO\n");
+ cc1101_strobe(dev, CC1101_SFRX);
+ }
+
+ return nbytes;
+ }
+
+ return 0;
+}
+
+
+int cc1101_write(struct cc1101_dev_s * dev, const uint8_t * buf, size_t size)
+{
+ uint8_t packetlen;
+
+ ASSERT(dev);
+ ASSERT(buf);
+
+ if (dev->flags & FLAGS_RXONLY) return -EPERM;
+
+ /* Present limit */
+ if (size > CC1101_PACKET_MAXDATALEN)
+ packetlen = CC1101_PACKET_MAXDATALEN;
+ else packetlen = size;
+
+ cc1101_access(dev, CC1101_TXFIFO, &packetlen, -1);
+ cc1101_access(dev, CC1101_TXFIFO, buf, -size);
+
+ return 0;
+}
+
+
+int cc1101_send(struct cc1101_dev_s * dev)
+{
+ ASSERT(dev);
+
+ if (dev->flags & FLAGS_RXONLY) return -EPERM;
+
+ cc1101_interrupt = 0;
+
+ cc1101_strobe(dev, CC1101_STX);
+
+ /* wait until send, going to IDLE */
+
+ while( cc1101_interrupt == 0 );
+
+ return 0;
+}
+
+
+int cc1101_idle(struct cc1101_dev_s * dev)
+{
+ ASSERT(dev);
+ cc1101_strobe(dev, CC1101_SIDLE);
+ return 0;
+}