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:
mb 2009-03-07 11:58:10 +00:00
parent acf64db483
commit df8b4f69f5
3 changed files with 131 additions and 70 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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_ */