source: trunk/package/ucmb/src/ucmb.c @ 14478

Last change on this file since 14478 was 14478, checked in by mb, 7 years ago

ucmb: Experimental SPI-GPIO based communication channel

File size: 7.0 KB
Line 
1/*
2 * Microcontroller Message Bus
3 *
4 * Copyright (c) 2009 Michael Buesch <mb@bu3sch.de>
5 *
6 * Licensed under the GNU/GPL. See COPYING for details.
7 */
8
9#include "ucmb.h"
10
11#include <linux/module.h>
12#include <linux/platform_device.h>
13#include <linux/miscdevice.h>
14#include <linux/fs.h>
15#include <linux/spi/spi.h>
16#include <linux/spi/spi_gpio.h>
17#include <linux/spi/spi_bitbang.h>
18#include <linux/gfp.h>
19
20
21#define PFX     "ucmb: "
22
23
24MODULE_LICENSE("GPL");
25MODULE_DESCRIPTION("Microcontroller Message Bus");
26MODULE_AUTHOR("Michael Buesch");
27
28
29struct ucmb {
30        /* Misc character device driver */
31        struct miscdevice mdev;
32        struct file_operations mdev_fops;
33
34        /* SPI driver */
35        struct spi_device *sdev;
36
37        /* SPI-GPIO driver */
38        struct spi_gpio_platform_data spi_gpio_pdata;
39        struct platform_device spi_gpio_pdev;
40};
41
42static int ucmb_spi_busnum_count = 1337;
43
44
45static struct ucmb_platform_data ucmb_list[] = {
46        { //FIXME don't define it here.
47                .name           = "ucmb",
48                .gpio_sck       = 0,
49                .gpio_miso      = 1,
50                .gpio_mosi      = 2,
51        },
52};
53
54
55static int __devinit ucmb_spi_probe(struct spi_device *sdev)
56{
57        return 0;
58}
59
60static int __devexit ucmb_spi_remove(struct spi_device *sdev)
61{
62        return 0;
63}
64
65static struct spi_driver ucmb_spi_driver = {
66        .driver         = {
67                .name   = "ucmb",
68                .bus    = &spi_bus_type,
69                .owner  = THIS_MODULE,
70        },
71        .probe          = ucmb_spi_probe,
72        .remove         = __devexit_p(ucmb_spi_remove),
73};
74
75static inline struct ucmb * filp_to_ucmb(struct file *filp)
76{
77        return container_of(filp->f_op, struct ucmb, mdev_fops);
78}
79
80/* FIXME offp */
81static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
82                         size_t size, loff_t *offp)
83{
84        struct ucmb *ucmb = filp_to_ucmb(filp);
85        u8 *buf;
86        int err;
87
88        err = -E2BIG;
89        if (size > PAGE_SIZE)
90                goto out;
91
92        err = -ENOMEM;
93        buf = (char *)__get_free_page(GFP_KERNEL);
94        if (!buf)
95                goto out;
96
97        err = spi_read(ucmb->sdev, buf, size);
98        if (err)
99                goto out_free;
100
101        err = -EFAULT;
102        if (copy_to_user(user_buf, buf, size))
103                goto out_free;
104
105        err = 0;
106
107out_free:
108        __free_page((unsigned long)buf);
109out:
110        return err ? err : size;
111}
112
113/* FIXME offp */
114static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
115                          size_t size, loff_t *offp)
116{
117        struct ucmb *ucmb = filp_to_ucmb(filp);
118        u8 *buf;
119        int err;
120
121        err = -ENOMEM;
122        buf = (char *)__get_free_page(GFP_KERNEL);
123        if (!buf)
124                goto out;
125
126        size = min(PAGE_SIZE, size);
127        err = -EFAULT;
128        if (copy_from_user(buf, user_buf, size))
129                goto out_free;
130
131        err = spi_write(ucmb->sdev, buf, size);
132
133out_free:
134        __free_page((unsigned long)buf);
135out:
136        return err ? err : size;
137}
138
139static int __devinit ucmb_probe(struct platform_device *pdev)
140{
141        struct ucmb_platform_data *pdata;
142        struct ucmb *ucmb;
143        int err;
144        const int bus_num = ucmb_spi_busnum_count++;
145        struct spi_bitbang *bb;
146
147        pdata = pdev->dev.platform_data;
148        if (!pdata)
149                return -ENXIO;
150
151        ucmb = kzalloc(sizeof(struct ucmb), GFP_KERNEL);
152        if (!ucmb)
153                return -ENOMEM;
154
155        /* Create the SPI GPIO bus master. */
156
157#ifdef CONFIG_SPI_GPIO_MODULE
158        err = request_module("spi_gpio");
159        if (err)
160                printk(KERN_WARNING PFX "Failed to request spi_gpio module\n");
161#endif /* CONFIG_SPI_GPIO_MODULE */
162
163        ucmb->spi_gpio_pdata.sck = pdata->gpio_sck;
164        ucmb->spi_gpio_pdata.mosi = pdata->gpio_mosi;
165        ucmb->spi_gpio_pdata.miso = pdata->gpio_miso;
166        ucmb->spi_gpio_pdata.num_chipselect = 1;
167
168        ucmb->spi_gpio_pdev.name = "spi_gpio";
169        ucmb->spi_gpio_pdev.id = bus_num;
170        ucmb->spi_gpio_pdev.dev.platform_data = &ucmb->spi_gpio_pdata;
171
172        err = platform_device_register(&ucmb->spi_gpio_pdev);
173        if (err) {
174                printk(KERN_ERR PFX "Failed to register SPI-GPIO platform device\n");
175                goto err_free_ucmb;
176        }
177        bb = platform_get_drvdata(&ucmb->spi_gpio_pdev);
178        if (!bb || !bb->master) {
179                printk(KERN_ERR PFX "No bitbanged master device found.\n");
180                goto err_unreg_spi_gpio_pdev;
181        }
182
183        /* Create the SPI device. */
184
185        ucmb->sdev = spi_alloc_device(bb->master);
186        if (!ucmb->sdev) {
187                printk(KERN_ERR PFX "Failed to allocate SPI device\n");
188                goto err_unreg_spi_gpio_pdev;
189        }
190        ucmb->sdev->max_speed_hz = 5000000;
191        ucmb->sdev->chip_select = 0;
192        ucmb->sdev->mode = SPI_MODE_0;
193        strlcpy(ucmb->sdev->modalias, "ucmb", /* We are the SPI driver. */
194                sizeof(ucmb->sdev->modalias));
195        ucmb->sdev->controller_data = (void *)SPI_GPIO_NO_CHIPSELECT;
196        err = spi_add_device(ucmb->sdev);
197        if (err) {
198                printk(KERN_ERR PFX "Failed to add SPI device\n");
199                goto err_free_spi_device;
200        }
201
202        /* Create the Misc char device. */
203
204        ucmb->mdev.minor = MISC_DYNAMIC_MINOR;
205        ucmb->mdev.name = pdata->name;
206        ucmb->mdev.parent = &pdev->dev;
207        ucmb->mdev_fops.read = ucmb_read;
208        ucmb->mdev_fops.write = ucmb_write;
209        ucmb->mdev.fops = &ucmb->mdev_fops;
210
211        err = misc_register(&ucmb->mdev);
212        if (err) {
213                printk(KERN_ERR PFX "Failed to register miscdev %s\n",
214                       ucmb->mdev.name);
215                goto err_unreg_spi_device;
216        }
217
218        platform_set_drvdata(pdev, ucmb);
219
220        printk(KERN_INFO PFX "Registered message bus \"%s\"\n", pdata->name);
221
222        return 0;
223
224err_unreg_spi_device:
225        spi_unregister_device(ucmb->sdev);
226err_free_spi_device:
227        spi_dev_put(ucmb->sdev);
228err_unreg_spi_gpio_pdev:
229        platform_device_unregister(&ucmb->spi_gpio_pdev);
230err_free_ucmb:
231        kfree(ucmb);
232
233        return err;
234}
235
236static int __devexit ucmb_remove(struct platform_device *pdev)
237{
238        struct ucmb *ucmb = platform_get_drvdata(pdev);
239        int err;
240
241        err = misc_deregister(&ucmb->mdev);
242        if (err) {
243                printk(KERN_ERR PFX "Failed to unregister miscdev %s\n",
244                       ucmb->mdev.name);
245        }
246        spi_unregister_device(ucmb->sdev);
247        spi_dev_put(ucmb->sdev);
248        platform_device_unregister(&ucmb->spi_gpio_pdev);
249
250        kfree(ucmb);
251        platform_set_drvdata(pdev, NULL);
252
253        return 0;
254}
255
256static struct platform_driver ucmb_driver = {
257        .driver         = {
258                .name   = "ucmb",
259                .owner  = THIS_MODULE,
260        },
261        .probe          = ucmb_probe,
262        .remove         = __devexit_p(ucmb_probe),
263};
264
265static int ucmb_modinit(void)
266{
267        struct ucmb_platform_data *pdata;
268        struct platform_device *pdev;
269        int err, i;
270
271        printk(KERN_INFO "Microcontroller message bus driver\n");
272
273        err = platform_driver_register(&ucmb_driver);
274        if (err) {
275                printk(KERN_ERR PFX "Failed to register platform driver\n");
276                return err;
277        }
278        err = spi_register_driver(&ucmb_spi_driver);
279        if (err) {
280                printk(KERN_ERR PFX "Failed to register SPI driver\n");
281                platform_driver_unregister(&ucmb_driver);
282                return err;
283        }
284
285        for (i = 0; i < ARRAY_SIZE(ucmb_list); i++) {
286                pdata = &ucmb_list[i];
287
288                pdev = platform_device_alloc("ucmb", i);
289                if (!pdev) {
290                        printk(KERN_ERR PFX "Failed to allocate platform device.\n");
291                        break;
292                }
293                err = platform_device_add_data(pdev, pdata, sizeof(*pdata));
294                if (err) {
295                        printk(KERN_ERR PFX "Failed to add platform data.\n");
296                        platform_device_put(pdev);
297                        break;
298                }
299                err = platform_device_add(pdev);
300                if (err) {
301                        printk(KERN_ERR PFX "Failed to register platform device.\n");
302                        platform_device_put(pdev);
303                        break;
304                }
305                pdata->pdev = pdev;
306        }
307
308        return 0;
309}
310module_init(ucmb_modinit);
311
312static void ucmb_modexit(void)
313{
314        struct ucmb_platform_data *pdata;
315        int i;
316
317        for (i = 0; i < ARRAY_SIZE(ucmb_list); i++) {
318                pdata = &ucmb_list[i];
319
320                if (pdata->pdev) {
321                        platform_device_unregister(pdata->pdev);
322                        platform_device_put(pdata->pdev);
323                }
324        }
325        spi_unregister_driver(&ucmb_spi_driver);
326        platform_driver_unregister(&ucmb_driver);
327}
328module_exit(ucmb_modexit);
Note: See TracBrowser for help on using the repository browser.