xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/rockchip_wlan/ssv6xxx/hci_wrapper/ssv_huw.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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, &regval);
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),&regval,sizeof(regval)));
150             }
151             else
152 #endif
153             {
154                 CHECK_RET(copy_to_user((int __user *)pucmd_data->out_data,&regval,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