]> rtime.felk.cvut.cz Git - socketcan-devel.git/blob - kernel/2.6/drivers/net/can/old/ccan/h7202_can.c
net/can bugfix: use after free bug in can protocol drivers
[socketcan-devel.git] / kernel / 2.6 / drivers / net / can / old / ccan / h7202_can.c
1 /*
2  * drivers/can/h7202_can.c
3  *
4  * Copyright (C) 2007
5  *
6  * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
7  * - Simon Kallweit, intefo AG
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the version 2 of the GNU General Public License
11  * as published by the Free Software Foundation
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/interrupt.h>
26 #include <linux/platform_device.h>
27 #include <linux/netdevice.h>
28 #include <linux/can.h>
29 #include <linux/can/dev.h>
30 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
31 #include <linux/io.h>
32 #else
33 #include <asm/io.h>
34 #endif
35 #include <asm/hardware.h>
36
37 #include "ccan.h"
38
39 #define DRV_NAME      "h7202can"
40 #define DELAY         5
41 #define CAN_ENABLE    0x0e
42
43 static u16 h7202can_read_reg(struct net_device *dev, enum c_regs reg)
44 {
45         u16 val;
46         volatile int i;
47
48         /* The big kernel lock is used to prevent any other AMBA devices from
49          * interfering with the current register read operation. The register
50          * is read twice because of braindamaged hynix cpu.
51          */
52         lock_kernel();
53         val = inw(dev->base_addr + (reg<<1));
54         for (i = 0; i < DELAY; i++);
55         val = inw(dev->base_addr + (reg<<1));
56         for (i = 0; i < DELAY; i++);
57         unlock_kernel();
58
59         return val;
60 }
61
62 static void h7202can_write_reg(struct net_device *dev, enum c_regs reg, u16 val)
63 {
64         volatile int i;
65
66         lock_kernel();
67         outw(val, dev->base_addr + (reg<<1));
68         for (i = 0; i < DELAY; i++);
69         unlock_kernel();
70 }
71
72 static int h7202can_drv_probe(struct platform_device *pdev)
73 {
74         struct net_device *dev;
75         struct ccan_priv *priv;
76         struct resource *mem;
77         u32 mem_size;
78         int ret = -ENODEV;
79
80         dev = alloc_ccandev(sizeof(struct ccan_priv));
81         if (!dev)
82                 return -ENOMEM;
83
84         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
85         dev->irq = platform_get_irq(pdev, 0);
86         if (!mem || !dev->irq)
87                 goto req_error;
88
89         mem_size = mem->end - mem->start + 1;
90         if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
91                 dev_err(&pdev->dev, "resource unavailable\n");
92                 goto req_error;
93         }
94
95         SET_NETDEV_DEV(dev, &pdev->dev);
96
97         dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
98
99         if (!dev->base_addr) {
100                 dev_err(&pdev->dev, "failed to map can port\n");
101                 ret = -ENOMEM;
102                 goto fail_map;
103         }
104
105         priv = netdev_priv(dev);
106         priv->can.can_sys_clock = 8000000;
107         priv->read_reg = h7202can_read_reg;
108         priv->write_reg = h7202can_write_reg;
109
110         platform_set_drvdata(pdev, dev);
111
112         /* configure ports */
113         switch (mem->start) {
114         case CAN0_PHYS:
115                 CPU_REG(GPIO_C_VIRT, GPIO_EN) &= ~(3<<1);
116                 CPU_REG(GPIO_C_VIRT, GPIO_DIR) &= ~(1<<1);
117                 CPU_REG(GPIO_C_VIRT, GPIO_DIR) |= (1<<2);
118                 break;
119         case CAN1_PHYS:
120                 CPU_REG(GPIO_E_VIRT, GPIO_EN) &= ~(3<<16);
121                 CPU_REG(GPIO_E_VIRT, GPIO_DIR) |= (1<<16);
122                 CPU_REG(GPIO_E_VIRT, GPIO_DIR) &= ~(1<<17);
123                 break;
124         }
125
126         /* enable can */
127         h7202can_write_reg(dev, CAN_ENABLE, 1);
128
129         ret = register_ccandev(dev);
130         if (ret >= 0) {
131                 dev_info(&pdev->dev, "probe for a port 0x%lX done\n",
132                          dev->base_addr);
133                 return ret;
134         }
135
136         iounmap((unsigned long *)dev->base_addr);
137 fail_map:
138         release_mem_region(mem->start, mem_size);
139 req_error:
140         free_ccandev(dev);
141         dev_err(&pdev->dev, "probe failed\n");
142         return ret;
143 }
144
145 static int h7202can_drv_remove(struct platform_device *pdev)
146 {
147         struct net_device *dev = platform_get_drvdata(pdev);
148         struct resource *mem;
149
150         platform_set_drvdata(pdev, NULL);
151         unregister_ccandev(dev);
152
153         iounmap((volatile void __iomem *)(dev->base_addr));
154         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
155         release_mem_region(mem->start, mem->end - mem->start + 1);
156         free_ccandev(dev);
157         return 0;
158 }
159
160 #ifdef CONFIG_PM
161 static int h7202can_drv_suspend(struct platform_device *pdev,
162                                 pm_message_t state)
163 {
164         return 0;
165 }
166
167 static int h7202can_drv_resume(struct platform_device *pdev)
168 {
169         return 0;
170 }
171 #endif /* CONFIG_PM */
172
173 static struct platform_driver h7202can_driver = {
174         .driver         = {
175                 .name           = DRV_NAME,
176         },
177         .probe          = h7202can_drv_probe,
178         .remove         = h7202can_drv_remove,
179 #ifdef CONFIG_PM
180         .suspend        = h7202can_drv_suspend,
181         .resume         = h7202can_drv_resume,
182 #endif  /* CONFIG_PM */
183 };
184
185 static int __init h7202can_init(void)
186 {
187         printk(KERN_INFO "%s initializing\n", h7202can_driver.driver.name);
188         return platform_driver_register(&h7202can_driver);
189 }
190
191 static void __exit h7202can_cleanup(void)
192 {
193         platform_driver_unregister(&h7202can_driver);
194         printk(KERN_INFO "%s unloaded\n", h7202can_driver.driver.name);
195 }
196
197 module_init(h7202can_init);
198 module_exit(h7202can_cleanup);
199
200 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
201 MODULE_AUTHOR("Simon Kallweit <simon.kallweit@intefo.ch>");
202 MODULE_LICENSE("GPL v2");
203 MODULE_DESCRIPTION("CAN port driver Hynix H7202 processor");