ucmb: Add msg-delay ioctls; Move device registration out of the driver; Add open-only-once sanity check.
git-svn-id: svn://svn.openwrt.org/openwrt/packages@14769 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
parent
acf64db483
commit
df8b4f69f5
@ -29,6 +29,8 @@
|
||||
|
||||
#define __UCMB_IOCTL ('U'|'C'|'M'|'B')
|
||||
#define UCMB_IOCTL_RESETUC _IO(__UCMB_IOCTL, 0)
|
||||
#define UCMB_IOCTL_GMSGDELAY _IOR(__UCMB_IOCTL, 1, unsigned int)
|
||||
#define UCMB_IOCTL_SMSGDELAY _IOW(__UCMB_IOCTL, 2, unsigned int)
|
||||
|
||||
|
||||
static void usage(int argc, char **argv)
|
||||
|
@ -43,7 +43,9 @@ MODULE_AUTHOR("Michael Buesch");
|
||||
struct ucmb {
|
||||
struct mutex mutex;
|
||||
|
||||
unsigned int msg_delay_ms;
|
||||
bool is_open;
|
||||
|
||||
unsigned int msg_delay_usec;
|
||||
unsigned int gpio_reset;
|
||||
bool reset_activelow;
|
||||
|
||||
@ -59,6 +61,9 @@ struct ucmb {
|
||||
struct platform_device spi_gpio_pdev;
|
||||
};
|
||||
|
||||
#define UCMB_MAX_MSG_DELAY (10 * 1000 * 1000) /* 10 seconds */
|
||||
|
||||
|
||||
struct ucmb_message_hdr {
|
||||
__le16 magic; /* UCMB_MAGIC */
|
||||
__le16 len; /* Payload length (excluding header and footer) */
|
||||
@ -85,22 +90,7 @@ enum ucmb_status_code {
|
||||
|
||||
|
||||
static int ucmb_spi_busnum_count = 1337;
|
||||
|
||||
|
||||
static struct ucmb_platform_data ucmb_list[] = {
|
||||
{ //FIXME don't define it here.
|
||||
.name = "ucmb",
|
||||
.gpio_cs = SPI_GPIO_NO_CHIPSELECT,
|
||||
.gpio_sck = 0,
|
||||
.gpio_miso = 1,
|
||||
.gpio_mosi = 2,
|
||||
.gpio_reset = 3,
|
||||
.reset_activelow = 0,
|
||||
.mode = SPI_MODE_0,
|
||||
.max_speed_hz = 128000, /* Hz */
|
||||
.msg_delay_ms = 1, /* mS */
|
||||
},
|
||||
};
|
||||
static int ucmb_pdev_id_count;
|
||||
|
||||
|
||||
static int __devinit ucmb_spi_probe(struct spi_device *sdev)
|
||||
@ -138,7 +128,7 @@ static int ucmb_reset_microcontroller(struct ucmb *ucmb)
|
||||
ucmb_toggle_reset_line(ucmb, 1);
|
||||
msleep(50);
|
||||
ucmb_toggle_reset_line(ucmb, 0);
|
||||
msleep(10);
|
||||
msleep(50);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -165,6 +155,38 @@ static inline struct ucmb * filp_to_ucmb(struct file *filp)
|
||||
return container_of(filp->f_op, struct ucmb, mdev_fops);
|
||||
}
|
||||
|
||||
static int ucmb_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct ucmb *ucmb = filp_to_ucmb(filp);
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&ucmb->mutex);
|
||||
|
||||
if (ucmb->is_open) {
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
ucmb->is_open = 1;
|
||||
ucmb->msg_delay_usec = 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&ucmb->mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ucmb_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct ucmb *ucmb = filp_to_ucmb(filp);
|
||||
|
||||
mutex_lock(&ucmb->mutex);
|
||||
WARN_ON(!ucmb->is_open);
|
||||
ucmb->is_open = 0;
|
||||
mutex_unlock(&ucmb->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucmb_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
@ -176,6 +198,26 @@ static int ucmb_ioctl(struct inode *inode, struct file *filp,
|
||||
case UCMB_IOCTL_RESETUC:
|
||||
ret = ucmb_reset_microcontroller(ucmb);
|
||||
break;
|
||||
case UCMB_IOCTL_GMSGDELAY:
|
||||
if (put_user(ucmb->msg_delay_usec, (unsigned int __user *)arg)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case UCMB_IOCTL_SMSGDELAY: {
|
||||
unsigned int msg_delay_usec;
|
||||
|
||||
if (get_user(msg_delay_usec, (unsigned int __user *)arg)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (msg_delay_usec > UCMB_MAX_MSG_DELAY) {
|
||||
ret = -E2BIG;
|
||||
break;
|
||||
}
|
||||
ucmb->msg_delay_usec = msg_delay_usec;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
@ -308,9 +350,16 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
/* The microcontroller deserves some time to process the message. */
|
||||
if (ucmb->msg_delay_ms)
|
||||
msleep(ucmb->msg_delay_ms);
|
||||
if (ucmb->msg_delay_usec) {
|
||||
/* The microcontroller deserves some time to process the message. */
|
||||
if (ucmb->msg_delay_usec >= 1000000) {
|
||||
ssleep(ucmb->msg_delay_usec / 1000000);
|
||||
msleep(DIV_ROUND_UP(ucmb->msg_delay_usec, 1000));
|
||||
} else if (ucmb->msg_delay_usec >= 1000) {
|
||||
msleep(DIV_ROUND_UP(ucmb->msg_delay_usec, 1000));
|
||||
} else
|
||||
udelay(ucmb->msg_delay_usec);
|
||||
}
|
||||
|
||||
/* Get the status code. */
|
||||
err = spi_read(ucmb->sdev, (u8 *)&status, sizeof(status));
|
||||
@ -351,7 +400,6 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
|
||||
if (!ucmb)
|
||||
return -ENOMEM;
|
||||
mutex_init(&ucmb->mutex);
|
||||
ucmb->msg_delay_ms = pdata->msg_delay_ms;
|
||||
ucmb->gpio_reset = pdata->gpio_reset;
|
||||
ucmb->reset_activelow = pdata->reset_activelow;
|
||||
|
||||
@ -426,6 +474,8 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
|
||||
ucmb->mdev.minor = MISC_DYNAMIC_MINOR;
|
||||
ucmb->mdev.name = pdata->name;
|
||||
ucmb->mdev.parent = &pdev->dev;
|
||||
ucmb->mdev_fops.open = ucmb_open;
|
||||
ucmb->mdev_fops.release = ucmb_release;
|
||||
ucmb->mdev_fops.read = ucmb_read;
|
||||
ucmb->mdev_fops.write = ucmb_write;
|
||||
ucmb->mdev_fops.ioctl = ucmb_ioctl;
|
||||
@ -490,67 +540,69 @@ static struct platform_driver ucmb_driver = {
|
||||
.remove = __devexit_p(ucmb_remove),
|
||||
};
|
||||
|
||||
int ucmb_device_register(struct ucmb_platform_data *pdata)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
int err;
|
||||
|
||||
pdev = platform_device_alloc("ucmb", ucmb_pdev_id_count++);
|
||||
if (!pdev) {
|
||||
printk(KERN_ERR PFX "Failed to allocate platform device.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
err = platform_device_add_data(pdev, pdata, sizeof(*pdata));
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Failed to add platform data.\n");
|
||||
platform_device_put(pdev);
|
||||
return err;
|
||||
}
|
||||
err = platform_device_add(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Failed to register platform device.\n");
|
||||
platform_device_put(pdev);
|
||||
return err;
|
||||
}
|
||||
pdata->pdev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ucmb_device_register);
|
||||
|
||||
void ucmb_device_unregister(struct ucmb_platform_data *pdata)
|
||||
{
|
||||
if (!pdata->pdev)
|
||||
return;
|
||||
platform_device_unregister(pdata->pdev);
|
||||
platform_device_put(pdata->pdev);
|
||||
pdata->pdev = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(ucmb_device_unregister);
|
||||
|
||||
static int ucmb_modinit(void)
|
||||
{
|
||||
struct ucmb_platform_data *pdata;
|
||||
struct platform_device *pdev;
|
||||
int err, i;
|
||||
int err;
|
||||
|
||||
printk(KERN_INFO "Microcontroller message bus driver\n");
|
||||
|
||||
err = platform_driver_register(&ucmb_driver);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Failed to register platform driver\n");
|
||||
return err;
|
||||
}
|
||||
err = spi_register_driver(&ucmb_spi_driver);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Failed to register SPI driver\n");
|
||||
platform_driver_unregister(&ucmb_driver);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ucmb_list); i++) {
|
||||
pdata = &ucmb_list[i];
|
||||
|
||||
pdev = platform_device_alloc("ucmb", i);
|
||||
if (!pdev) {
|
||||
printk(KERN_ERR PFX "Failed to allocate platform device.\n");
|
||||
break;
|
||||
}
|
||||
err = platform_device_add_data(pdev, pdata, sizeof(*pdata));
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Failed to add platform data.\n");
|
||||
platform_device_put(pdev);
|
||||
break;
|
||||
}
|
||||
err = platform_device_add(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Failed to register platform device.\n");
|
||||
platform_device_put(pdev);
|
||||
break;
|
||||
}
|
||||
pdata->pdev = pdev;
|
||||
err = platform_driver_register(&ucmb_driver);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Failed to register platform driver\n");
|
||||
spi_unregister_driver(&ucmb_spi_driver);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(ucmb_modinit);
|
||||
subsys_initcall(ucmb_modinit);
|
||||
|
||||
static void ucmb_modexit(void)
|
||||
{
|
||||
struct ucmb_platform_data *pdata;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ucmb_list); i++) {
|
||||
pdata = &ucmb_list[i];
|
||||
|
||||
if (pdata->pdev) {
|
||||
platform_device_unregister(pdata->pdev);
|
||||
platform_device_put(pdata->pdev);
|
||||
}
|
||||
}
|
||||
spi_unregister_driver(&ucmb_spi_driver);
|
||||
platform_driver_unregister(&ucmb_driver);
|
||||
spi_unregister_driver(&ucmb_spi_driver);
|
||||
}
|
||||
module_exit(ucmb_modexit);
|
||||
|
@ -10,10 +10,18 @@
|
||||
|
||||
/** UCMB_IOCTL_RESETUC - Reset the microcontroller. */
|
||||
#define UCMB_IOCTL_RESETUC _IO(__UCMB_IOCTL, 0)
|
||||
/** UCMB_IOCTL_GMSGDELAY - Get the delay to wait before fetching the status. */
|
||||
#define UCMB_IOCTL_GMSGDELAY _IOR(__UCMB_IOCTL, 1, unsigned int)
|
||||
/** UCMB_IOCTL_SMSGDELAY - Set the delay to wait before fetching the status. */
|
||||
#define UCMB_IOCTL_SMSGDELAY _IOW(__UCMB_IOCTL, 2, unsigned int)
|
||||
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_gpio.h>
|
||||
|
||||
/**
|
||||
* struct ucmb_platform_data - UCMB device descriptor
|
||||
*
|
||||
@ -33,9 +41,6 @@
|
||||
*
|
||||
* @mode: The SPI bus mode. SPI_MODE_*
|
||||
* @max_speed_hz: The bus speed, in Hz. If zero the speed is not limited.
|
||||
* @msg_delay_ms: The message delay time, in milliseconds.
|
||||
* This is the time the microcontroller takes to process
|
||||
* one message.
|
||||
*/
|
||||
struct ucmb_platform_data {
|
||||
const char *name;
|
||||
@ -50,13 +55,15 @@ struct ucmb_platform_data {
|
||||
|
||||
u8 mode;
|
||||
u32 max_speed_hz;
|
||||
unsigned int msg_delay_ms;
|
||||
|
||||
struct platform_device *pdev; /* internal */
|
||||
};
|
||||
|
||||
#define UCMB_NO_RESET ((unsigned int)-1)
|
||||
|
||||
int ucmb_device_register(struct ucmb_platform_data *pdata);
|
||||
void ucmb_device_unregister(struct ucmb_platform_data *pdata);
|
||||
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* LINUX_UCMB_H_ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user