1 /*
2 * Copyright (c) 2015 South Silicon Valley Microelectronics Inc.
3 * Copyright (c) 2015 iComm Corporation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <linux/version.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <linux/fs.h>
22 #include <linux/cdev.h>
23 #include <linux/slab.h>
24 #include <linux/skbuff.h>
25 #include <linux/wait.h>
26 #include <linux/netdevice.h>
27 #include <hci/ssv_hci.h>
28 #include <hwif/hwif.h>
29 #include "ssv_huw.h"
30 #define SSV6200_ID_NUMBER (128)
31 #define BLOCKSIZE 0x40
32 #define RXBUFLENGTH 1024*3
33 #define RXBUFSIZE 512
34 #define CHECK_RET(_fun) \
35 do { \
36 if (0 != _fun) \
37 printk("File = %s\nLine = %d\nFunc=%s\nDate=%s\nTime=%s\n", __FILE__, __LINE__, __FUNCTION__, __DATE__, __TIME__); \
38 } while (0)
39 #define SMAC_SRAM_WRITE(_s,_r,_v,_sz) \
40 (_s)->hci.hci_ops->hci_write_sram(_r, _v, _sz)
41 #define SMAC_REG_WRITE(_s,_r,_v) \
42 (_s)->hci.hci_ops->hci_write_word(_r, _v)
43 #define SMAC_REG_READ(_s,_r,_v) \
44 (_s)->hci.hci_ops->hci_read_word(_r, _v)
45 #define HCI_START(_sh) \
46 (_sh)->hci.hci_ops->hci_start()
47 #define HCI_STOP(_sh) \
48 (_sh)->hci.hci_ops->hci_stop()
49 #define HCI_SEND(_sh,_sk,_q) \
50 (_sh)->hci.hci_ops->hci_tx(_sk, _q, HCI_FLAGS_NO_FLOWCTRL)
51 #define HCI_PAUSE(_sh,_mk) \
52 (_sh)->hci.hci_ops->hci_tx_pause(_mk)
53 #define HCI_RESUME(_sh,_mk) \
54 (_sh)->hci.hci_ops->hci_tx_resume(_mk)
55 #define HCI_TXQ_FLUSH(_sh,_mk) \
56 (_sh)->hci.hci_ops->hci_txq_flush(_mk)
57 #define HCI_TXQ_FLUSH_BY_STA(_sh,_aid) \
58 (_sh)->hci.hci_ops->hci_txq_flush_by_sta(_aid)
59 #define HCI_TXQ_EMPTY(_sh,_txqid) \
60 (_sh)->hci.hci_ops->hci_txq_empty(_txqid)
61 #define HCI_WAKEUP_PMU(_sh) \
62 (_sh)->hci.hci_ops->hci_pmu_wakeup()
63 #define HCI_SEND_CMD(_sh,_sk) \
64 (_sh)->hci.hci_ops->hci_send_cmd(_sk)
65 struct ssv_huw_dev {
66 struct device *dev;
67 struct ssv6xxx_platform_data *priv;
68 struct ssv6xxx_hci_info hci;
69 char chip_id[24];
70 u64 chip_tag;
71 u8 funcFocus;
72 wait_queue_head_t read_wq;
73 spinlock_t rxlock;
74 void *bufaddr;
75 struct sk_buff_head rx_skb_q;
76 };
77 struct ssv_rxbuf
78 {
79 struct list_head list;
80 u32 rxsize;
81 u8 rxdata[RXBUFLENGTH];
82 };
83 struct ssv_huw_dev g_huw_dev;
84 static unsigned int ssv_sdiobridge_ioctl_major = 0;
85 static unsigned int num_of_dev = 1;
86 static struct cdev ssv_sdiobridge_ioctl_cdev;
87 static struct class *fc;
88 #if !defined(USE_THREAD_RX) || defined(USE_BATCH_RX)
ssv_huw_rx(struct sk_buff_head * rx_skb_q,void * args)89 int ssv_huw_rx(struct sk_buff_head *rx_skb_q, void *args)
90 #else
91 int ssv_huw_rx(struct sk_buff *rx_skb, void *args)
92 #endif
93 {
94 struct ssv_huw_dev *phuw_dev = (struct ssv_huw_dev *)args;
95 unsigned long flags;
96 spin_lock_irqsave(&phuw_dev->rxlock, flags);
97 #if !defined(USE_THREAD_RX) || defined(USE_BATCH_RX)
98 while (skb_queue_len(rx_skb_q))
99 __skb_queue_tail(&phuw_dev->rx_skb_q, __skb_dequeue(rx_skb_q));
100 #else
101 __skb_queue_tail(&phuw_dev->rx_skb_q, rx_skb);
102 #endif
103 spin_unlock_irqrestore(&phuw_dev->rxlock, flags);
104 wake_up_interruptible(&phuw_dev->read_wq);
105 return 0;
106 }
ssv_huw_txbuf_free_skb(struct sk_buff * skb,void * args)107 void ssv_huw_txbuf_free_skb(struct sk_buff *skb, void *args)
108 {
109 if (!skb)
110 return;
111 dev_kfree_skb_any(skb);
112 }
skb_queue_len_bhsafe(struct sk_buff_head * head,spinlock_t * plock)113 unsigned int skb_queue_len_bhsafe(struct sk_buff_head *head, spinlock_t *plock)
114 {
115 unsigned int len = 0;
116 spin_lock_bh(plock);
117 len = skb_queue_len(head);
118 spin_unlock_bh(plock);
119 return len;
120 }
ssv_huw_ioctl_readReg(struct ssv_huw_dev * phuw_dev,unsigned int cmd,struct ssv_huw_cmd * pcmd_data,struct ssv_huw_cmd * pucmd_data,bool isCompat)121 static long ssv_huw_ioctl_readReg(struct ssv_huw_dev *phuw_dev,unsigned int cmd, struct ssv_huw_cmd *pcmd_data,struct ssv_huw_cmd *pucmd_data,bool isCompat)
122 {
123 long retval =0;
124 if ( pcmd_data->in_data_len < 4 || pcmd_data->out_data_len < 4)
125 {
126 retval = -1;
127 }
128 else
129 {
130 u32 tmpdata;
131 u32 regval;
132 int ret = 0;
133 #ifdef CONFIG_COMPAT
134 if ( isCompat )
135 {
136 CHECK_RET(copy_from_user(&tmpdata,(int __user *)compat_ptr((unsigned long)pucmd_data->in_data),sizeof(tmpdata)));
137 }
138 else
139 #endif
140 {
141 CHECK_RET(copy_from_user(&tmpdata,(int __user *)pucmd_data->in_data,sizeof(tmpdata)));
142 }
143 ret = SMAC_REG_READ(phuw_dev, tmpdata, ®val);
144 if ( !ret )
145 {
146 #ifdef CONFIG_COMPAT
147 if ( isCompat )
148 {
149 CHECK_RET(copy_to_user((int __user *)compat_ptr((unsigned long)pucmd_data->out_data),®val,sizeof(regval)));
150 }
151 else
152 #endif
153 {
154 CHECK_RET(copy_to_user((int __user *)pucmd_data->out_data,®val,sizeof(regval)));
155 }
156 }
157 else
158 {
159 dev_err(phuw_dev->dev,"%s: error : %d",__FUNCTION__,ret);
160 retval = -1;
161 }
162 }
163 return retval;
164 }
ssv_huw_ioctl_writeReg(struct ssv_huw_dev * phuw_dev,unsigned int cmd,struct ssv_huw_cmd * pcmd_data,struct ssv_huw_cmd * pucmd_data,bool isCompat)165 static long ssv_huw_ioctl_writeReg(struct ssv_huw_dev *phuw_dev,unsigned int cmd, struct ssv_huw_cmd *pcmd_data,struct ssv_huw_cmd *pucmd_data,bool isCompat)
166 {
167 long retval =0;
168 if ( pcmd_data->in_data_len < 8)
169 {
170 retval = -1;
171 }
172 else
173 {
174 u32 tmpdata[2];
175 int ret = 0;
176 #ifdef CONFIG_COMPAT
177 if ( isCompat )
178 {
179 CHECK_RET(copy_from_user(&tmpdata,(int __user *)compat_ptr((unsigned long)pucmd_data->in_data),sizeof(tmpdata)));
180 }
181 else
182 #endif
183 {
184 CHECK_RET(copy_from_user(&tmpdata,(int __user *)pucmd_data->in_data,sizeof(tmpdata)));
185 }
186 SMAC_REG_WRITE(phuw_dev, tmpdata[0], tmpdata[1]);
187 if ( ret )
188 {
189 dev_err(phuw_dev->dev,"%s: error : %d",__FUNCTION__,ret);
190 retval = -1;
191 }
192 }
193 return retval;
194 }
ssv_huw_ioctl_writeSram(struct ssv_huw_dev * phuw_dev,unsigned int cmd,struct ssv_huw_cmd * pcmd_data,struct ssv_huw_cmd * pucmd_data,bool isCompat)195 static long ssv_huw_ioctl_writeSram(struct ssv_huw_dev *phuw_dev,unsigned int cmd, struct ssv_huw_cmd *pcmd_data,struct ssv_huw_cmd *pucmd_data,bool isCompat)
196 {
197 long retval =0;
198 unsigned char *ptr = NULL;
199 unsigned int addr;
200 if (( pcmd_data->in_data_len != 4) || ( pcmd_data->out_data_len <= 0))
201 {
202 retval = -1;
203 }
204 else
205 {
206 int ret = 0;
207 ptr = kzalloc(pcmd_data->out_data_len, GFP_KERNEL);
208 if(ptr == NULL)
209 return -ENOMEM;
210 #ifdef CONFIG_COMPAT
211 if ( isCompat )
212 {
213 CHECK_RET(copy_from_user(&addr, (int __user *)compat_ptr((unsigned long)pucmd_data->in_data), sizeof(addr)));
214 CHECK_RET(copy_from_user(ptr, (int __user *)compat_ptr((unsigned long)pucmd_data->out_data), pcmd_data->out_data_len));
215 }
216 else
217 #endif
218 {
219 CHECK_RET(copy_from_user(&addr, (int __user *)pucmd_data->in_data, sizeof(addr)));
220 CHECK_RET(copy_from_user(ptr, (int __user *)pucmd_data->out_data, pcmd_data->out_data_len));
221 }
222 SMAC_SRAM_WRITE(phuw_dev, addr, ptr, pcmd_data->out_data_len);
223 if ( ret )
224 {
225 dev_err(phuw_dev->dev,"%s: error : %d",__FUNCTION__,ret);
226 retval = -1;
227 }
228 kfree(ptr);
229 }
230 return retval;
231 }
ssv_huw_ioctl_process(struct ssv_huw_dev * glue,unsigned int cmd,struct ssv_huw_cmd * pucmd_data,bool isCompat)232 static long ssv_huw_ioctl_process(struct ssv_huw_dev *glue, unsigned int cmd, struct ssv_huw_cmd *pucmd_data, bool isCompat)
233 {
234 struct ssv_huw_cmd cmd_data;
235 long retval=0;
236 if ( isCompat )
237 {
238 CHECK_RET(copy_from_user(&cmd_data,(int __user *)pucmd_data,sizeof(*pucmd_data)));
239 }
240 else
241 {
242 CHECK_RET(copy_from_user(&cmd_data,(int __user *)pucmd_data,sizeof(*pucmd_data)));
243 }
244 switch (cmd)
245 {
246 case IOCTL_SSVSDIO_READ_REG:
247 retval = ssv_huw_ioctl_readReg(glue,cmd,&cmd_data,pucmd_data,isCompat);
248 break;
249 case IOCTL_SSVSDIO_WRITE_REG:
250 retval = ssv_huw_ioctl_writeReg(glue,cmd,&cmd_data,pucmd_data,isCompat);
251 break;
252 case IOCTL_SSVSDIO_WRITE_SRAM:
253 retval = ssv_huw_ioctl_writeSram(glue,cmd,&cmd_data,pucmd_data,isCompat);
254 break;
255 case IOCTL_SSVSDIO_START:
256 retval = HCI_START(glue);
257 break;
258 case IOCTL_SSVSDIO_STOP:
259 retval = HCI_STOP(glue);
260 break;
261 default:
262 return -EINVAL;
263 }
264 return retval;
265 }
266 #ifdef CONFIG_COMPAT
ssv_huw_compat_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)267 static long ssv_huw_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
268 {
269 long retval=0;
270 struct ssv_huw_cmd *pucmd_data;
271 pucmd_data = (struct ssv_huw_cmd *)arg;
272 retval = ssv_huw_ioctl_process(&g_huw_dev, cmd, pucmd_data, true);
273 return retval;
274 }
275 #endif
ssv_huw_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)276 static long ssv_huw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
277 {
278 long retval=0;
279 struct ssv_huw_cmd *pucmd_data;
280 pucmd_data = (struct ssv_huw_cmd *)arg;
281 retval = ssv_huw_ioctl_process( &g_huw_dev,cmd,pucmd_data,false);
282 return retval;
283 }
ssv_huw_open(struct inode * inode,struct file * fp)284 static int ssv_huw_open(struct inode *inode, struct file *fp)
285 {
286 fp->private_data = &g_huw_dev;
287 return 0;
288 }
ssv_huw_release(struct inode * inode,struct file * fp)289 static int ssv_huw_release(struct inode *inode, struct file *fp)
290 {
291 struct sk_buff *skb = NULL;
292 while((skb = skb_dequeue(&(g_huw_dev.rx_skb_q))) != NULL)
293 {
294 dev_kfree_skb_any(skb);
295 skb = NULL;
296 }
297 return 0;
298 }
ssv_huw_read(struct file * fp,char __user * buf,size_t length,loff_t * offset)299 static ssize_t ssv_huw_read(struct file *fp, char __user * buf, size_t length, loff_t * offset)
300 {
301 int ret = 0, copy_length = 0;
302 struct sk_buff *skb = NULL;
303 if (skb_queue_len_bhsafe(&(g_huw_dev.rx_skb_q), &(g_huw_dev.rxlock)) == 0)
304 {
305 ret = wait_event_interruptible((g_huw_dev.read_wq), (skb_queue_len_bhsafe(&(g_huw_dev.rx_skb_q), &(g_huw_dev.rxlock)) != 0));
306 if (ret != 0)
307 return -1;
308 }
309 spin_lock_bh(&(g_huw_dev.rxlock));
310 if (skb_queue_len(&(g_huw_dev.rx_skb_q)) > 0)
311 skb = skb_dequeue(&(g_huw_dev.rx_skb_q));
312 spin_unlock_bh(&(g_huw_dev.rxlock));
313 if (skb != NULL)
314 {
315 copy_length = min(skb->len,(u32)length);
316 CHECK_RET(copy_to_user((int __user *)buf, skb->data, copy_length));
317 dev_kfree_skb_any(skb);
318 }
319 return copy_length;
320 }
ssv_huw_write(struct file * fp,const char __user * buf,size_t length,loff_t * offset)321 static ssize_t ssv_huw_write(struct file *fp, const char __user * buf, size_t length, loff_t * offset)
322 {
323 struct sk_buff *skb;
324 unsigned int len = (unsigned int)length;
325 len = (len & 0x1f)?(((len>>5) + 1)<<5):len;
326 skb = __dev_alloc_skb(len, GFP_KERNEL);
327 if (skb == NULL)
328 {
329 dev_err(g_huw_dev.dev,"%s: error : alloc buf error size:%d",__FUNCTION__,(u32)len);
330 return -ENOMEM;
331 }
332 CHECK_RET(copy_from_user(skb->data, (int __user *)buf, length));
333 skb_put(skb, length);
334 HCI_SEND(&g_huw_dev, skb, 1);
335 return length;
336 }
ssv_huw_tx_cb(struct sk_buff_head * skb_head,void * args)337 void ssv_huw_tx_cb(struct sk_buff_head *skb_head, void *args)
338 {
339 struct sk_buff *skb = NULL;
340 while ((skb=skb_dequeue(skb_head)))
341 {
342 dev_kfree_skb_any(skb);
343 skb = NULL;
344 }
345 }
ssv_huw_read_hci_info(struct ssv_huw_dev * phuw_dev)346 int ssv_huw_read_hci_info(struct ssv_huw_dev *phuw_dev)
347 {
348 struct ssv6xxx_hci_info *pinfo = &(phuw_dev->hci);
349 pinfo->hci_ops = NULL;
350 pinfo->dev = phuw_dev->dev;
351 pinfo->hci_rx_cb = ssv_huw_rx;
352 pinfo->rx_cb_args = (void *)phuw_dev;
353 pinfo->hci_tx_cb= ssv_huw_tx_cb;
354 pinfo->tx_cb_args = NULL;
355 pinfo->hci_skb_update_cb = NULL;
356 pinfo->skb_update_args = NULL;
357 pinfo->hci_tx_flow_ctrl_cb = NULL;
358 pinfo->tx_fctrl_cb_args = NULL;
359 pinfo->hci_tx_q_empty_cb = NULL;
360 pinfo->tx_q_empty_args = NULL;
361 pinfo->hci_tx_buf_free_cb = ssv_huw_txbuf_free_skb;
362 pinfo->tx_buf_free_args = NULL;
363 pinfo->if_ops = phuw_dev->priv->ops;
364 return 0;
365 }
366 struct file_operations s_huw_ops =
367 {
368 .read = ssv_huw_read,
369 .write = ssv_huw_write,
370 .unlocked_ioctl = ssv_huw_ioctl,
371 #ifdef CONFIG_COMPAT
372 .compat_ioctl = ssv_huw_compat_ioctl,
373 #endif
374 .open = ssv_huw_open,
375 .release = ssv_huw_release,
376 };
ssv_huw_init_buf(struct ssv_huw_dev * hdev)377 static int ssv_huw_init_buf(struct ssv_huw_dev *hdev)
378 {
379 init_waitqueue_head(&hdev->read_wq);
380 spin_lock_init(&hdev->rxlock);
381 skb_queue_head_init(&(hdev->rx_skb_q));
382 return 0;
383 }
384 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
ssv_huw_devnode(struct device * dev,umode_t * mode)385 static char *ssv_huw_devnode(struct device *dev, umode_t *mode)
386 #else
387 static char *ssv_huw_devnode(struct device *dev, mode_t *mode)
388 #endif
389 {
390 if (!mode)
391 return NULL;
392 *mode = 0666;
393 return NULL;
394 }
ssv_huw_probe(struct platform_device * pdev)395 int ssv_huw_probe(struct platform_device *pdev)
396 {
397 dev_t dev;
398 int alloc_ret = 0;
399 int cdev_ret = 0;
400 if (!pdev->dev.platform_data) {
401 dev_err(&pdev->dev, "no platform data specified!\n");
402 return -EINVAL;
403 }
404 ssv_huw_init_buf(&g_huw_dev);
405 g_huw_dev.priv = (pdev->dev.platform_data);
406 g_huw_dev.dev = &(pdev->dev);
407 ssv_huw_read_hci_info(&g_huw_dev);
408 ssv6xxx_hci_register(&(g_huw_dev.hci));
409 dev = MKDEV(ssv_sdiobridge_ioctl_major, 0);
410 alloc_ret = alloc_chrdev_region(&dev, 0, num_of_dev, FILE_DEVICE_SSVSDIO_NAME);
411 if (alloc_ret)
412 goto error;
413 ssv_sdiobridge_ioctl_major = MAJOR(dev);
414 cdev_init(&ssv_sdiobridge_ioctl_cdev, &s_huw_ops);
415 cdev_ret = cdev_add(&ssv_sdiobridge_ioctl_cdev, dev, num_of_dev);
416 if (cdev_ret)
417 goto error;
418 fc=class_create(THIS_MODULE, FILE_DEVICE_SSVSDIO_NAME);
419 fc->devnode = ssv_huw_devnode;
420 device_create(fc,NULL,dev,NULL,"%s",FILE_DEVICE_SSVSDIO_NAME);
421 dev_err(&pdev->dev, "%s driver(major: %d) installed.\n", FILE_DEVICE_SSVSDIO_NAME, ssv_sdiobridge_ioctl_major);
422 return 0;
423 error:
424 if (cdev_ret == 0)
425 cdev_del(&ssv_sdiobridge_ioctl_cdev);
426 if (alloc_ret == 0)
427 unregister_chrdev_region(dev, num_of_dev);
428 return -ENODEV;
429 }
430 EXPORT_SYMBOL(ssv_huw_probe);
ssv_huw_remove(struct platform_device * pdev)431 int ssv_huw_remove(struct platform_device *pdev)
432 {
433 dev_t dev;
434 int ret = 0;
435 ssv6xxx_hci_deregister();
436 memset(&g_huw_dev, 0 , sizeof(g_huw_dev));
437 dev = MKDEV(ssv_sdiobridge_ioctl_major, 0);
438 device_destroy(fc,dev);
439 class_destroy(fc);
440 cdev_del(&ssv_sdiobridge_ioctl_cdev);
441 unregister_chrdev_region(dev, num_of_dev);
442 return ret;
443 }
444 EXPORT_SYMBOL(ssv_huw_remove);
445 static const struct platform_device_id huw_id_table[] = {
446 {
447 .name = "ssv6200",
448 .driver_data = 0x00,
449 },
450 {},
451 };
452 MODULE_DEVICE_TABLE(platform, huw_id_table);
453 static struct platform_driver ssv_huw_driver =
454 {
455 .probe = ssv_huw_probe,
456 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
457 .remove = __devexit_p(ssv_huw_remove),
458 #else
459 .remove = ssv_huw_remove,
460 #endif
461 .id_table = huw_id_table,
462 .driver = {
463 .name = "SSV WLAN driver",
464 .owner = THIS_MODULE,
465 }
466 };
467 #if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
ssv_huw_init(void)468 int ssv_huw_init(void)
469 #else
470 static int __init ssv_huw_init(void)
471 #endif
472 {
473 int ret;
474 memset(&g_huw_dev, 0 , sizeof(g_huw_dev));
475 ret = platform_driver_register(&ssv_huw_driver);
476 if (ret < 0)
477 {
478 printk(KERN_ALERT "[HCI user-space wrapper]: Fail to register huw\n");
479 }
480 return ret;
481 }
482 #if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
ssv_huw_exit(void)483 void ssv_huw_exit(void)
484 #else
485 static void __exit ssv_huw_exit(void)
486 #endif
487 {
488 platform_driver_unregister(&ssv_huw_driver);
489 }
490 #if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
491 EXPORT_SYMBOL(ssv_huw_init);
492 EXPORT_SYMBOL(ssv_huw_exit);
493 #else
494 module_init(ssv_huw_init);
495 module_exit(ssv_huw_exit);
496 #endif
497 MODULE_LICENSE("GPL");
498