]>
Commit | Line | Data |
---|---|---|
f7c1be0c MB |
1 | //--------------------------------------------------------------------------- |
2 | // FT1000 driver for Flarion Flash OFDM NIC Device | |
3 | // | |
4 | // Copyright (C) 2006 Flarion Technologies, All rights reserved. | |
5 | // | |
bf3146c8 | 6 | // This program is free software; you can redistribute it and/or modify it |
f7c1be0c | 7 | // under the terms of the GNU General Public License as published by the Free |
bf3146c8 GKH |
8 | // Software Foundation; either version 2 of the License, or (at your option) any |
9 | // later version. This program is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
11 | // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | // more details. You should have received a copy of the GNU General Public | |
13 | // License along with this program; if not, write to the | |
14 | // Free Software Foundation, Inc., 59 Temple Place - | |
15 | // Suite 330, Boston, MA 02111-1307, USA. | |
f7c1be0c MB |
16 | //--------------------------------------------------------------------------- |
17 | // | |
18 | // File: ft1000_chdev.c | |
19 | // | |
20 | // Description: Custom character device dispatch routines. | |
21 | // | |
22 | // History: | |
23 | // 8/29/02 Whc Ported to Linux. | |
24 | // 6/05/06 Whc Porting to Linux 2.6.9 | |
25 | // | |
26 | //--------------------------------------------------------------------------- | |
27 | #include <linux/module.h> | |
28 | #include <linux/kernel.h> | |
29 | #include <linux/sched.h> | |
30 | #include <linux/signal.h> | |
31 | #include <linux/errno.h> | |
32 | #include <linux/poll.h> | |
33 | #include <linux/netdevice.h> | |
34 | #include <linux/delay.h> | |
35 | ||
36 | #include <linux/fs.h> | |
37 | #include <linux/kmod.h> | |
38 | #include <linux/ioctl.h> | |
39 | #include <linux/unistd.h> | |
40 | ||
41 | #include "ft1000_usb.h" | |
42 | //#include "ft1000_ioctl.h" | |
43 | ||
2a953cfd | 44 | static int ft1000_flarion_cnt = 0; |
f7c1be0c | 45 | |
2a953cfd | 46 | //need to looking usage of ft1000Handle |
f7c1be0c | 47 | |
bf3146c8 | 48 | static int ft1000_ChOpen (struct inode *Inode, struct file *File); |
f7c1be0c | 49 | static unsigned int ft1000_ChPoll(struct file *file, poll_table *wait); |
4fea0749 | 50 | static long ft1000_ChIoctl(struct file *File, unsigned int Command, |
bf3146c8 | 51 | unsigned long Argument); |
f7c1be0c MB |
52 | static int ft1000_ChRelease (struct inode *Inode, struct file *File); |
53 | ||
f7c1be0c MB |
54 | // Global pointer to device object |
55 | static struct ft1000_device *pdevobj[MAX_NUM_CARDS + 2]; | |
56 | //static devfs_handle_t ft1000Handle[MAX_NUM_CARDS]; | |
57 | ||
58 | // List to free receive command buffer pool | |
59 | struct list_head freercvpool; | |
60 | ||
61 | // lock to arbitrate free buffer list for receive command data | |
bf3146c8 | 62 | spinlock_t free_buff_lock; |
f7c1be0c MB |
63 | |
64 | int numofmsgbuf = 0; | |
65 | ||
66 | // Global variable to indicate that all provisioning data is sent to DSP | |
67 | //BOOLEAN fProvComplete; | |
68 | ||
69 | // | |
70 | // Table of entry-point routines for char device | |
71 | // | |
72 | static struct file_operations ft1000fops = | |
73 | { | |
d16044cf AB |
74 | .unlocked_ioctl = ft1000_ChIoctl, |
75 | .poll = ft1000_ChPoll, | |
76 | .open = ft1000_ChOpen, | |
77 | .release = ft1000_ChRelease, | |
78 | .llseek = no_llseek, | |
f7c1be0c MB |
79 | }; |
80 | ||
81 | ||
82 | ||
83 | ||
84 | //--------------------------------------------------------------------------- | |
85 | // Function: exec_mknod | |
86 | // | |
bf3146c8 | 87 | // Parameters: |
f7c1be0c MB |
88 | // |
89 | // Returns: | |
90 | // | |
91 | // Description: | |
92 | // | |
93 | // Notes: | |
94 | // | |
95 | //--------------------------------------------------------------------------- | |
bf3146c8 | 96 | static int exec_mknod (void *pdata) |
f7c1be0c | 97 | { |
1a88a068 | 98 | struct ft1000_info *info; |
f7c1be0c MB |
99 | char mjnum[4]; |
100 | char minornum[4]; | |
bf3146c8 | 101 | char temp[32]; |
f7c1be0c MB |
102 | int retcode; |
103 | // int i; //aelias [-] reason : unused variable | |
104 | char *envp[] = { "HOME=/", "PATH=/usr/bin:/bin", NULL }; | |
105 | char *argv[]={"-m 666",temp,"c",mjnum,minornum,NULL}; | |
106 | ||
107 | info = pdata; | |
108 | DEBUG("ft1000_chdev:exec_mknod is called with major number = %d\n", info->DeviceMajor); | |
109 | sprintf(temp, "%s%s", "/dev/", info->DeviceName) ; | |
110 | sprintf(mjnum, "%d", info->DeviceMajor); | |
111 | sprintf(minornum, "%d", info->CardNumber); | |
bf3146c8 | 112 | |
f7c1be0c MB |
113 | //char *argv[]={"mknod","-m 666",temp,"c",mjnum,minornum,NULL}; |
114 | // char *argv[]={"-m 666",temp,"c",mjnum,minornum,NULL}; | |
115 | ||
116 | //for (i=0; i<7;i++) | |
117 | // DEBUG("argv[%d]=%s\n", i, argv[i]); | |
118 | ||
bf3146c8 | 119 | |
f7c1be0c MB |
120 | retcode = call_usermodehelper ("/bin/mknod", argv, envp, 1); |
121 | if (retcode) { | |
122 | DEBUG("ft1000_chdev:exec_mknod failed to make the node: retcode = %d\n", retcode); | |
123 | } | |
124 | ||
bf3146c8 | 125 | |
f7c1be0c MB |
126 | |
127 | return retcode; | |
128 | ||
129 | } | |
130 | ||
131 | //--------------------------------------------------------------------------- | |
132 | // Function: rm_mknod | |
133 | // | |
134 | // Description: This module removes the FT1000 device file | |
135 | // | |
136 | //--------------------------------------------------------------------------- | |
bf3146c8 | 137 | static int rm_mknod (void *pdata) |
f7c1be0c | 138 | { |
bf3146c8 | 139 | |
1a88a068 | 140 | struct ft1000_info *info; |
f7c1be0c MB |
141 | //char *argv[4]={"rm", "-f", "/dev/FT1000", NULL}; |
142 | int retcode; | |
bf3146c8 | 143 | char temp[32]; |
f7c1be0c MB |
144 | char *argv[]={"rm", "-f", temp, NULL}; |
145 | ||
1a88a068 | 146 | info = (struct ft1000_info *)pdata; |
f7c1be0c MB |
147 | DEBUG("ft1000_chdev:rm_mknod is called for device %s\n", info->DeviceName); |
148 | sprintf(temp, "%s%s", "/dev/", info->DeviceName) ; | |
bf3146c8 | 149 | |
f7c1be0c | 150 | // char *argv[]={"rm", "-f", temp, NULL}; |
bf3146c8 | 151 | |
f7c1be0c MB |
152 | retcode = call_usermodehelper ("/bin/rm", argv, NULL, 1); |
153 | if (retcode) { | |
154 | DEBUG("ft1000_chdev:rm_mknod failed to remove the node: retcode = %d\n", retcode); | |
155 | } | |
156 | else | |
157 | DEBUG("ft1000_chdev:rm_mknod done!\n"); | |
158 | ||
159 | ||
160 | return retcode; | |
161 | ||
162 | } | |
163 | //--------------------------------------------------------------------------- | |
164 | // Function: ft1000_get_buffer | |
165 | // | |
bf3146c8 | 166 | // Parameters: |
f7c1be0c MB |
167 | // |
168 | // Returns: | |
169 | // | |
170 | // Description: | |
171 | // | |
172 | // Notes: | |
173 | // | |
174 | //--------------------------------------------------------------------------- | |
29437ab0 | 175 | struct dpram_blk *ft1000_get_buffer(struct list_head *bufflist) |
f7c1be0c MB |
176 | { |
177 | unsigned long flags; | |
29437ab0 | 178 | struct dpram_blk *ptr; |
bf3146c8 | 179 | |
f7c1be0c MB |
180 | spin_lock_irqsave(&free_buff_lock, flags); |
181 | // Check if buffer is available | |
182 | if ( list_empty(bufflist) ) { | |
183 | DEBUG("ft1000_get_buffer: No more buffer - %d\n", numofmsgbuf); | |
184 | ptr = NULL; | |
185 | } | |
186 | else { | |
187 | numofmsgbuf--; | |
29437ab0 | 188 | ptr = list_entry(bufflist->next, struct dpram_blk, list); |
f7c1be0c MB |
189 | list_del(&ptr->list); |
190 | //DEBUG("ft1000_get_buffer: number of free msg buffers = %d\n", numofmsgbuf); | |
191 | } | |
192 | spin_unlock_irqrestore(&free_buff_lock, flags); | |
193 | ||
194 | return ptr; | |
195 | } | |
196 | ||
197 | ||
198 | ||
199 | ||
200 | //--------------------------------------------------------------------------- | |
201 | // Function: ft1000_free_buffer | |
202 | // | |
bf3146c8 | 203 | // Parameters: |
f7c1be0c MB |
204 | // |
205 | // Returns: | |
206 | // | |
207 | // Description: | |
208 | // | |
209 | // Notes: | |
210 | // | |
211 | //--------------------------------------------------------------------------- | |
29437ab0 | 212 | void ft1000_free_buffer(struct dpram_blk *pdpram_blk, struct list_head *plist) |
f7c1be0c MB |
213 | { |
214 | unsigned long flags; | |
bf3146c8 | 215 | |
f7c1be0c MB |
216 | spin_lock_irqsave(&free_buff_lock, flags); |
217 | // Put memory back to list | |
218 | list_add_tail(&pdpram_blk->list, plist); | |
219 | numofmsgbuf++; | |
220 | //DEBUG("ft1000_free_buffer: number of free msg buffers = %d\n", numofmsgbuf); | |
221 | spin_unlock_irqrestore(&free_buff_lock, flags); | |
222 | } | |
223 | ||
224 | //--------------------------------------------------------------------------- | |
225 | // Function: ft1000_CreateDevice | |
226 | // | |
227 | // Parameters: dev - pointer to adapter object | |
228 | // | |
229 | // Returns: 0 if successful | |
230 | // | |
231 | // Description: Creates a private char device. | |
232 | // | |
233 | // Notes: Only called by init_module(). | |
234 | // | |
235 | //--------------------------------------------------------------------------- | |
236 | int ft1000_CreateDevice(struct ft1000_device *dev) | |
237 | { | |
1a88a068 | 238 | struct ft1000_info *info = netdev_priv(dev->net); |
f7c1be0c MB |
239 | int result; |
240 | int i; | |
241 | pid_t pid; | |
242 | ||
243 | // make a new device name | |
244 | sprintf(info->DeviceName, "%s%d", "FT100", info->CardNumber); | |
245 | ||
246 | // Delete any existing FT1000 node | |
247 | pid = kernel_thread (rm_mknod,(void *)info, 0); | |
248 | msleep(1000); | |
bf3146c8 | 249 | |
f7c1be0c MB |
250 | DEBUG("ft1000_CreateDevice: number of instance = %d\n", ft1000_flarion_cnt); |
251 | DEBUG("DeviceCreated = %x\n", info->DeviceCreated); | |
bf3146c8 | 252 | |
f7c1be0c MB |
253 | //save the device info to global array |
254 | pdevobj[info->CardNumber] = dev; | |
255 | ||
7cfd8a37 | 256 | DEBUG("ft1000_CreateDevice: ******SAVED pdevobj[%d]=%p\n", info->CardNumber, pdevobj[info->CardNumber]); //aelias [+] reason:up |
bf3146c8 | 257 | |
f7c1be0c MB |
258 | if (info->DeviceCreated) |
259 | { | |
260 | DEBUG("ft1000_CreateDevice: \"%s\" already registered\n", info->DeviceName); | |
261 | return -EIO; | |
262 | } | |
263 | ||
264 | ||
265 | // register the device | |
266 | DEBUG("ft1000_CreateDevice: \"%s\" device registration\n", info->DeviceName); | |
267 | info->DeviceMajor = 0; | |
bf3146c8 | 268 | |
f7c1be0c MB |
269 | result = register_chrdev(info->DeviceMajor, info->DeviceName, &ft1000fops); |
270 | if (result < 0) | |
271 | { | |
272 | DEBUG("ft1000_CreateDevice: unable to get major %d\n", info->DeviceMajor); | |
273 | return result; | |
274 | } | |
275 | ||
276 | DEBUG("ft1000_CreateDevice: registered char device \"%s\"\n", info->DeviceName); | |
277 | ||
278 | // save a dynamic device major number | |
279 | if (info->DeviceMajor == 0) | |
280 | { | |
281 | info->DeviceMajor = result; | |
282 | DEBUG("ft1000_PcdCreateDevice: device major = %d\n", info->DeviceMajor); | |
283 | } | |
284 | ||
285 | // Create a thread to call user mode app to mknod | |
286 | pid = kernel_thread (exec_mknod, (void *)info, 0); | |
bf3146c8 GKH |
287 | |
288 | // initialize application information | |
bf3146c8 | 289 | |
f7c1be0c | 290 | // if (ft1000_flarion_cnt == 0) { |
bf3146c8 | 291 | // |
f7c1be0c MB |
292 | // DEBUG("Initialize free_buff_lock and freercvpool\n"); |
293 | // spin_lock_init(&free_buff_lock); | |
294 | // | |
295 | // // initialize a list of buffers to be use for queuing up receive command data | |
296 | // INIT_LIST_HEAD (&freercvpool); | |
297 | // | |
298 | // // create list of free buffers | |
299 | // for (i=0; i<NUM_OF_FREE_BUFFERS; i++) { | |
300 | // // Get memory for DPRAM_DATA link list | |
29437ab0 | 301 | // pdpram_blk = kmalloc ( sizeof(struct dpram_blk), GFP_KERNEL ); |
f7c1be0c | 302 | // // Get a block of memory to store command data |
bf3146c8 GKH |
303 | // pdpram_blk->pbuffer = kmalloc ( MAX_CMD_SQSIZE, GFP_KERNEL ); |
304 | // // link provisioning data | |
f7c1be0c MB |
305 | // list_add_tail (&pdpram_blk->list, &freercvpool); |
306 | // } | |
307 | // numofmsgbuf = NUM_OF_FREE_BUFFERS; | |
308 | // } | |
f7c1be0c | 309 | |
bf3146c8 GKH |
310 | |
311 | // initialize application information | |
f7c1be0c MB |
312 | info->appcnt = 0; |
313 | for (i=0; i<MAX_NUM_APP; i++) { | |
314 | info->app_info[i].nTxMsg = 0; | |
315 | info->app_info[i].nRxMsg = 0; | |
316 | info->app_info[i].nTxMsgReject = 0; | |
317 | info->app_info[i].nRxMsgMiss = 0; | |
2a953cfd | 318 | info->app_info[i].fileobject = NULL; |
f7c1be0c MB |
319 | info->app_info[i].app_id = i+1; |
320 | info->app_info[i].DspBCMsgFlag = 0; | |
321 | info->app_info[i].NumOfMsg = 0; | |
322 | init_waitqueue_head(&info->app_info[i].wait_dpram_msg); | |
323 | INIT_LIST_HEAD (&info->app_info[i].app_sqlist); | |
324 | } | |
325 | ||
326 | ||
327 | ||
328 | ||
bf3146c8 | 329 | // ft1000Handle[info->CardNumber] = devfs_register(NULL, info->DeviceName, DEVFS_FL_AUTO_DEVNUM, 0, 0, |
f7c1be0c MB |
330 | // S_IFCHR | S_IRUGO | S_IWUGO, &ft1000fops, NULL); |
331 | ||
332 | ||
333 | info->DeviceCreated = TRUE; | |
334 | ft1000_flarion_cnt++; | |
335 | ||
336 | return result; | |
337 | } | |
338 | ||
339 | //--------------------------------------------------------------------------- | |
340 | // Function: ft1000_DestroyDeviceDEBUG | |
341 | // | |
342 | // Parameters: dev - pointer to adapter object | |
343 | // | |
344 | // Description: Destroys a private char device. | |
345 | // | |
346 | // Notes: Only called by cleanup_module(). | |
347 | // | |
348 | //--------------------------------------------------------------------------- | |
349 | void ft1000_DestroyDevice(struct net_device *dev) | |
350 | { | |
1a88a068 | 351 | struct ft1000_info *info = netdev_priv(dev); |
f7c1be0c MB |
352 | int result = 0; |
353 | pid_t pid; | |
354 | int i; | |
29437ab0 MB |
355 | struct dpram_blk *pdpram_blk; |
356 | struct dpram_blk *ptr; | |
bf3146c8 | 357 | |
f7c1be0c MB |
358 | DEBUG("ft1000_chdev:ft1000_DestroyDevice called\n"); |
359 | ||
bf3146c8 GKH |
360 | |
361 | ||
f7c1be0c MB |
362 | if (info->DeviceCreated) |
363 | { | |
bf3146c8 | 364 | ft1000_flarion_cnt--; |
f7c1be0c | 365 | unregister_chrdev(info->DeviceMajor, info->DeviceName); |
bf3146c8 | 366 | DEBUG("ft1000_DestroyDevice: unregistered device \"%s\", result = %d\n", |
f7c1be0c MB |
367 | info->DeviceName, result); |
368 | ||
369 | pid = kernel_thread (rm_mknod, (void *)info, 0); | |
370 | ||
371 | // Make sure we free any memory reserve for slow Queue | |
372 | for (i=0; i<MAX_NUM_APP; i++) { | |
373 | while (list_empty(&info->app_info[i].app_sqlist) == 0) { | |
29437ab0 | 374 | pdpram_blk = list_entry(info->app_info[i].app_sqlist.next, struct dpram_blk, list); |
f7c1be0c MB |
375 | list_del(&pdpram_blk->list); |
376 | ft1000_free_buffer(pdpram_blk, &freercvpool); | |
bf3146c8 | 377 | |
f7c1be0c MB |
378 | } |
379 | wake_up_interruptible(&info->app_info[i].wait_dpram_msg); | |
380 | } | |
381 | ||
382 | // Remove buffer allocated for receive command data | |
383 | if (ft1000_flarion_cnt == 0) { | |
384 | while (list_empty(&freercvpool) == 0) { | |
29437ab0 | 385 | ptr = list_entry(freercvpool.next, struct dpram_blk, list); |
f7c1be0c MB |
386 | list_del(&ptr->list); |
387 | kfree(ptr->pbuffer); | |
bf3146c8 | 388 | kfree(ptr); |
f7c1be0c MB |
389 | } |
390 | } | |
391 | ||
392 | // devfs_unregister(ft1000Handle[info->CardNumber]); | |
393 | ||
394 | info->DeviceCreated = FALSE; | |
bf3146c8 | 395 | |
f7c1be0c MB |
396 | pdevobj[info->CardNumber] = NULL; |
397 | } | |
bf3146c8 GKH |
398 | |
399 | ||
f7c1be0c MB |
400 | } |
401 | ||
402 | //--------------------------------------------------------------------------- | |
403 | // Function: ft1000_ChOpen | |
404 | // | |
bf3146c8 | 405 | // Parameters: |
f7c1be0c | 406 | // |
bf3146c8 | 407 | // Description: |
f7c1be0c MB |
408 | // |
409 | // Notes: | |
410 | // | |
411 | //--------------------------------------------------------------------------- | |
bf3146c8 | 412 | static int ft1000_ChOpen (struct inode *Inode, struct file *File) |
f7c1be0c | 413 | { |
1a88a068 | 414 | struct ft1000_info *info; |
f7c1be0c | 415 | int i,num; |
bf3146c8 | 416 | |
f7c1be0c MB |
417 | DEBUG("ft1000_ChOpen called\n"); |
418 | num = (MINOR(Inode->i_rdev) & 0xf); | |
419 | DEBUG("ft1000_ChOpen: minor number=%d\n", num); | |
bf3146c8 | 420 | |
f7c1be0c | 421 | for (i=0; i<5; i++) |
7cfd8a37 | 422 | DEBUG("pdevobj[%d]=%p\n", i, pdevobj[i]); //aelias [+] reason: down |
f7c1be0c MB |
423 | |
424 | if ( pdevobj[num] != NULL ) | |
1a88a068 MB |
425 | //info = (struct ft1000_info *)(pdevobj[num]->net->priv); |
426 | info = (struct ft1000_info *)netdev_priv(pdevobj[num]->net); | |
f7c1be0c MB |
427 | else |
428 | { | |
429 | DEBUG("ft1000_ChOpen: can not find device object %d\n", num); | |
430 | return -1; | |
431 | } | |
432 | ||
7cfd8a37 | 433 | DEBUG("f_owner = %p number of application = %d\n", (&File->f_owner), info->appcnt ); |
f7c1be0c MB |
434 | |
435 | // Check if maximum number of application exceeded | |
436 | if (info->appcnt > MAX_NUM_APP) { | |
437 | DEBUG("Maximum number of application exceeded\n"); | |
438 | return -EACCES; | |
439 | } | |
440 | ||
441 | // Search for available application info block | |
442 | for (i=0; i<MAX_NUM_APP; i++) { | |
7cfd8a37 | 443 | if ( (info->app_info[i].fileobject == NULL) ) { |
f7c1be0c MB |
444 | break; |
445 | } | |
446 | } | |
447 | ||
448 | // Fail due to lack of application info block | |
449 | if (i == MAX_NUM_APP) { | |
450 | DEBUG("Could not find an application info block\n"); | |
451 | return -EACCES; | |
452 | } | |
453 | ||
454 | info->appcnt++; | |
7cfd8a37 | 455 | info->app_info[i].fileobject = &File->f_owner; |
f7c1be0c MB |
456 | info->app_info[i].nTxMsg = 0; |
457 | info->app_info[i].nRxMsg = 0; | |
458 | info->app_info[i].nTxMsgReject = 0; | |
459 | info->app_info[i].nRxMsgMiss = 0; | |
460 | ||
461 | File->private_data = pdevobj[num]->net; | |
462 | ||
d16044cf | 463 | nonseekable_open(Inode, File); |
f7c1be0c MB |
464 | return 0; |
465 | } | |
466 | ||
467 | ||
468 | //--------------------------------------------------------------------------- | |
469 | // Function: ft1000_ChPoll | |
470 | // | |
bf3146c8 | 471 | // Parameters: |
f7c1be0c | 472 | // |
bf3146c8 | 473 | // Description: |
f7c1be0c MB |
474 | // |
475 | // Notes: | |
476 | // | |
477 | //--------------------------------------------------------------------------- | |
478 | ||
479 | static unsigned int ft1000_ChPoll(struct file *file, poll_table *wait) | |
480 | { | |
481 | struct net_device *dev = file->private_data; | |
1a88a068 | 482 | struct ft1000_info *info; |
f7c1be0c | 483 | int i; |
bf3146c8 | 484 | |
f7c1be0c MB |
485 | //DEBUG("ft1000_ChPoll called\n"); |
486 | if (ft1000_flarion_cnt == 0) { | |
487 | DEBUG("FT1000:ft1000_ChPoll called when ft1000_flarion_cnt is zero\n"); | |
488 | return (-EBADF); | |
489 | } | |
490 | ||
1a88a068 | 491 | info = (struct ft1000_info *) netdev_priv(dev); |
bf3146c8 | 492 | |
f7c1be0c MB |
493 | // Search for matching file object |
494 | for (i=0; i<MAX_NUM_APP; i++) { | |
7cfd8a37 | 495 | if ( info->app_info[i].fileobject == &file->f_owner) { |
f7c1be0c MB |
496 | //DEBUG("FT1000:ft1000_ChIoctl: Message is for AppId = %d\n", info->app_info[i].app_id); |
497 | break; | |
498 | } | |
499 | } | |
500 | ||
501 | // Could not find application info block | |
502 | if (i == MAX_NUM_APP) { | |
503 | DEBUG("FT1000:ft1000_ChIoctl:Could not find application info block\n"); | |
504 | return ( -EACCES ); | |
bf3146c8 GKH |
505 | } |
506 | ||
f7c1be0c MB |
507 | if (list_empty(&info->app_info[i].app_sqlist) == 0) { |
508 | DEBUG("FT1000:ft1000_ChPoll:Message detected in slow queue\n"); | |
509 | return(POLLIN | POLLRDNORM | POLLPRI); | |
510 | } | |
bf3146c8 | 511 | |
f7c1be0c MB |
512 | poll_wait (file, &info->app_info[i].wait_dpram_msg, wait); |
513 | //DEBUG("FT1000:ft1000_ChPoll:Polling for data from DSP\n"); | |
514 | ||
515 | return (0); | |
516 | } | |
517 | ||
518 | //--------------------------------------------------------------------------- | |
519 | // Function: ft1000_ChIoctl | |
520 | // | |
bf3146c8 | 521 | // Parameters: |
f7c1be0c | 522 | // |
bf3146c8 | 523 | // Description: |
f7c1be0c MB |
524 | // |
525 | // Notes: | |
526 | // | |
527 | //--------------------------------------------------------------------------- | |
4fea0749 | 528 | static long ft1000_ChIoctl (struct file *File, unsigned int Command, |
bf3146c8 | 529 | unsigned long Argument) |
f7c1be0c | 530 | { |
2a953cfd | 531 | void __user *argp = (void __user *)Argument; |
f7c1be0c | 532 | struct net_device *dev; |
1a88a068 | 533 | struct ft1000_info *info; |
f7c1be0c MB |
534 | struct ft1000_device *ft1000dev; |
535 | int result=0; | |
536 | int cmd; | |
bf3146c8 GKH |
537 | int i; |
538 | u16 tempword; | |
539 | unsigned long flags; | |
f7c1be0c MB |
540 | struct timeval tv; |
541 | IOCTL_GET_VER get_ver_data; | |
542 | IOCTL_GET_DSP_STAT get_stat_data; | |
543 | u8 ConnectionMsg[] = {0x00,0x44,0x10,0x20,0x80,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x93,0x64, | |
544 | 0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0a, | |
545 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
546 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
547 | 0x00,0x00,0x02,0x37,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x01,0x00,0x01,0x7f,0x00, | |
548 | 0x00,0x01,0x00,0x00}; | |
549 | ||
550 | unsigned short ledStat=0; | |
bf3146c8 GKH |
551 | unsigned short conStat=0; |
552 | ||
f7c1be0c MB |
553 | //DEBUG("ft1000_ChIoctl called\n"); |
554 | ||
555 | if (ft1000_flarion_cnt == 0) { | |
556 | DEBUG("FT1000:ft1000_ChIoctl called when ft1000_flarion_cnt is zero\n"); | |
557 | return (-EBADF); | |
558 | } | |
559 | ||
560 | //DEBUG("FT1000:ft1000_ChIoctl:Command = 0x%x Argument = 0x%8x\n", Command, (u32)Argument); | |
561 | ||
562 | dev = File->private_data; | |
1a88a068 | 563 | info = (struct ft1000_info *) netdev_priv(dev); |
f7c1be0c MB |
564 | ft1000dev = info->pFt1000Dev; |
565 | cmd = _IOC_NR(Command); | |
566 | //DEBUG("FT1000:ft1000_ChIoctl:cmd = 0x%x\n", cmd); | |
bf3146c8 | 567 | |
f7c1be0c MB |
568 | // process the command |
569 | switch (cmd) { | |
570 | case IOCTL_REGISTER_CMD: | |
571 | DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_REGISTER called\n"); | |
2a953cfd | 572 | result = get_user(tempword, (__u16 __user*)argp); |
f7c1be0c MB |
573 | if (result) { |
574 | DEBUG("result = %d failed to get_user\n", result); | |
575 | break; | |
576 | } | |
577 | if (tempword == DSPBCMSGID) { | |
578 | // Search for matching file object | |
579 | for (i=0; i<MAX_NUM_APP; i++) { | |
7cfd8a37 | 580 | if ( info->app_info[i].fileobject == &File->f_owner) { |
f7c1be0c MB |
581 | info->app_info[i].DspBCMsgFlag = 1; |
582 | DEBUG("FT1000:ft1000_ChIoctl:Registered for broadcast messages\n"); | |
583 | break; | |
584 | } | |
585 | } | |
586 | } | |
587 | break; | |
bf3146c8 | 588 | |
f7c1be0c MB |
589 | case IOCTL_GET_VER_CMD: |
590 | DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_GET_VER called\n"); | |
591 | ||
592 | get_ver_data.drv_ver = FT1000_DRV_VER; | |
593 | ||
2a953cfd | 594 | if (copy_to_user(argp, &get_ver_data, sizeof(get_ver_data)) ) { |
f7c1be0c MB |
595 | DEBUG("FT1000:ft1000_ChIoctl: copy fault occurred\n"); |
596 | result = -EFAULT; | |
597 | break; | |
598 | } | |
599 | ||
600 | DEBUG("FT1000:ft1000_ChIoctl:driver version = 0x%x\n",(unsigned int)get_ver_data.drv_ver); | |
601 | ||
602 | break; | |
603 | case IOCTL_CONNECT: | |
604 | // Connect Message | |
605 | DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_CONNECT\n"); | |
606 | ConnectionMsg[79] = 0xfc; | |
bf3146c8 GKH |
607 | CardSendCommand(ft1000dev, (unsigned short *)ConnectionMsg, 0x4c); |
608 | ||
f7c1be0c MB |
609 | break; |
610 | case IOCTL_DISCONNECT: | |
611 | // Disconnect Message | |
612 | DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_DISCONNECT\n"); | |
613 | ConnectionMsg[79] = 0xfd; | |
bf3146c8 | 614 | CardSendCommand(ft1000dev, (unsigned short *)ConnectionMsg, 0x4c); |
f7c1be0c MB |
615 | break; |
616 | case IOCTL_GET_DSP_STAT_CMD: | |
617 | //DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_GET_DSP_STAT called\n"); | |
86ee1e49 | 618 | memset(&get_stat_data, 0, sizeof(get_stat_data)); |
f7c1be0c MB |
619 | memcpy(get_stat_data.DspVer, info->DspVer, DSPVERSZ); |
620 | memcpy(get_stat_data.HwSerNum, info->HwSerNum, HWSERNUMSZ); | |
621 | memcpy(get_stat_data.Sku, info->Sku, SKUSZ); | |
622 | memcpy(get_stat_data.eui64, info->eui64, EUISZ); | |
623 | ||
624 | if (info->ProgConStat != 0xFF) { | |
625 | ft1000_read_dpram16(ft1000dev, FT1000_MAG_DSP_LED, (PUCHAR)&ledStat, FT1000_MAG_DSP_LED_INDX); | |
626 | get_stat_data.LedStat = ntohs(ledStat); | |
627 | DEBUG("FT1000:ft1000_ChIoctl: LedStat = 0x%x\n", get_stat_data.LedStat); | |
628 | ft1000_read_dpram16(ft1000dev, FT1000_MAG_DSP_CON_STATE, (PUCHAR)&conStat, FT1000_MAG_DSP_CON_STATE_INDX); | |
629 | get_stat_data.ConStat = ntohs(conStat); | |
630 | DEBUG("FT1000:ft1000_ChIoctl: ConStat = 0x%x\n", get_stat_data.ConStat); | |
631 | } | |
632 | else { | |
633 | get_stat_data.ConStat = 0x0f; | |
634 | } | |
bf3146c8 GKH |
635 | |
636 | ||
f7c1be0c MB |
637 | get_stat_data.nTxPkts = info->stats.tx_packets; |
638 | get_stat_data.nRxPkts = info->stats.rx_packets; | |
639 | get_stat_data.nTxBytes = info->stats.tx_bytes; | |
640 | get_stat_data.nRxBytes = info->stats.rx_bytes; | |
641 | do_gettimeofday ( &tv ); | |
642 | get_stat_data.ConTm = (u32)(tv.tv_sec - info->ConTm); | |
bf3146c8 | 643 | DEBUG("Connection Time = %d\n", (int)get_stat_data.ConTm); |
2a953cfd | 644 | if (copy_to_user(argp, &get_stat_data, sizeof(get_stat_data)) ) { |
f7c1be0c MB |
645 | DEBUG("FT1000:ft1000_ChIoctl: copy fault occurred\n"); |
646 | result = -EFAULT; | |
647 | break; | |
648 | } | |
649 | DEBUG("ft1000_chioctl: GET_DSP_STAT succeed\n"); | |
bf3146c8 | 650 | break; |
f7c1be0c MB |
651 | case IOCTL_SET_DPRAM_CMD: |
652 | { | |
7cfd8a37 | 653 | IOCTL_DPRAM_BLK *dpram_data; |
f7c1be0c MB |
654 | //IOCTL_DPRAM_COMMAND dpram_command; |
655 | USHORT qtype; | |
656 | USHORT msgsz; | |
b13e39b2 | 657 | struct pseudo_hdr *ppseudo_hdr; |
f7c1be0c MB |
658 | PUSHORT pmsg; |
659 | USHORT total_len; | |
660 | USHORT app_index; | |
661 | u16 status; | |
662 | ||
663 | //DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_SET_DPRAM called\n"); | |
bf3146c8 | 664 | |
f7c1be0c MB |
665 | |
666 | if (ft1000_flarion_cnt == 0) { | |
667 | return (-EBADF); | |
668 | } | |
bf3146c8 | 669 | |
f7c1be0c MB |
670 | if (info->DrvMsgPend) { |
671 | return (-ENOTTY); | |
672 | } | |
673 | ||
674 | if ( (info->DspAsicReset) || (info->fProvComplete == 0) ) { | |
675 | return (-EACCES); | |
676 | } | |
677 | ||
678 | info->fAppMsgPend = 1; | |
bf3146c8 | 679 | |
f7c1be0c | 680 | if (info->CardReady) { |
bf3146c8 | 681 | |
f7c1be0c MB |
682 | //DEBUG("FT1000:ft1000_ChIoctl: try to SET_DPRAM \n"); |
683 | ||
684 | // Get the length field to see how many bytes to copy | |
2a953cfd | 685 | result = get_user(msgsz, (__u16 __user *)argp); |
f7c1be0c MB |
686 | msgsz = ntohs (msgsz); |
687 | //DEBUG("FT1000:ft1000_ChIoctl: length of message = %d\n", msgsz); | |
bf3146c8 | 688 | |
f7c1be0c MB |
689 | if (msgsz > MAX_CMD_SQSIZE) { |
690 | DEBUG("FT1000:ft1000_ChIoctl: bad message length = %d\n", msgsz); | |
691 | result = -EINVAL; | |
692 | break; | |
693 | } | |
694 | ||
7cfd8a37 AB |
695 | result = -ENOMEM; |
696 | dpram_data = kmalloc(msgsz + 2, GFP_KERNEL); | |
697 | if (!dpram_data) | |
698 | break; | |
699 | ||
f7c1be0c | 700 | //if ( copy_from_user(&(dpram_command.dpram_blk), (PIOCTL_DPRAM_BLK)Argument, msgsz+2) ) { |
2a953cfd | 701 | if ( copy_from_user(&dpram_data, argp, msgsz+2) ) { |
f7c1be0c MB |
702 | DEBUG("FT1000:ft1000_ChIoctl: copy fault occurred\n"); |
703 | result = -EFAULT; | |
704 | } | |
705 | else { | |
706 | #if 0 | |
707 | // whc - for debugging only | |
708 | ptr = (char *)&dpram_data; | |
709 | for (i=0; i<msgsz; i++) { | |
710 | DEBUG(1,"FT1000:ft1000_ChIoctl: data %d = 0x%x\n", i, *ptr++); | |
711 | } | |
712 | #endif | |
713 | // Check if this message came from a registered application | |
714 | for (i=0; i<MAX_NUM_APP; i++) { | |
7cfd8a37 | 715 | if ( info->app_info[i].fileobject == &File->f_owner) { |
f7c1be0c MB |
716 | break; |
717 | } | |
718 | } | |
719 | if (i==MAX_NUM_APP) { | |
720 | DEBUG("FT1000:No matching application fileobject\n"); | |
721 | result = -EINVAL; | |
7cfd8a37 | 722 | kfree(dpram_data); |
f7c1be0c MB |
723 | break; |
724 | } | |
725 | app_index = i; | |
726 | ||
727 | // Check message qtype type which is the lower byte within qos_class | |
728 | //qtype = ntohs(dpram_command.dpram_blk.pseudohdr.qos_class) & 0xff; | |
7cfd8a37 | 729 | qtype = ntohs(dpram_data->pseudohdr.qos_class) & 0xff; |
f7c1be0c MB |
730 | //DEBUG("FT1000_ft1000_ChIoctl: qtype = %d\n", qtype); |
731 | if (qtype) { | |
bf3146c8 GKH |
732 | } |
733 | else { | |
f7c1be0c MB |
734 | // Put message into Slow Queue |
735 | // Only put a message into the DPRAM if msg doorbell is available | |
736 | status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL); | |
737 | //DEBUG("FT1000_ft1000_ChIoctl: READ REGISTER tempword=%x\n", tempword); | |
738 | if (tempword & FT1000_DB_DPRAM_TX) { | |
739 | // Suspend for 2ms and try again due to DSP doorbell busy | |
740 | mdelay(2); | |
741 | status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL); | |
742 | if (tempword & FT1000_DB_DPRAM_TX) { | |
743 | // Suspend for 1ms and try again due to DSP doorbell busy | |
744 | mdelay(1); | |
745 | status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL); | |
746 | if (tempword & FT1000_DB_DPRAM_TX) { | |
747 | status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL); | |
748 | if (tempword & FT1000_DB_DPRAM_TX) { | |
749 | // Suspend for 3ms and try again due to DSP doorbell busy | |
750 | mdelay(3); | |
751 | status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL); | |
752 | if (tempword & FT1000_DB_DPRAM_TX) { | |
753 | DEBUG("FT1000:ft1000_ChIoctl:Doorbell not available\n"); | |
754 | result = -ENOTTY; | |
7cfd8a37 | 755 | kfree(dpram_data); |
f7c1be0c MB |
756 | break; |
757 | } | |
758 | } | |
759 | } | |
760 | } | |
761 | } | |
bf3146c8 | 762 | |
f7c1be0c | 763 | //DEBUG("FT1000_ft1000_ChIoctl: finished reading register\n"); |
bf3146c8 | 764 | |
f7c1be0c MB |
765 | // Make sure we are within the limits of the slow queue memory limitation |
766 | if ( (msgsz < MAX_CMD_SQSIZE) && (msgsz > PSEUDOSZ) ) { | |
767 | // Need to put sequence number plus new checksum for message | |
768 | //pmsg = (PUSHORT)&dpram_command.dpram_blk.pseudohdr; | |
7cfd8a37 | 769 | pmsg = (PUSHORT)&dpram_data->pseudohdr; |
b13e39b2 | 770 | ppseudo_hdr = (struct pseudo_hdr *)pmsg; |
f7c1be0c MB |
771 | total_len = msgsz+2; |
772 | if (total_len & 0x1) { | |
773 | total_len++; | |
774 | } | |
bf3146c8 | 775 | |
f7c1be0c MB |
776 | // Insert slow queue sequence number |
777 | ppseudo_hdr->seq_num = info->squeseqnum++; | |
778 | ppseudo_hdr->portsrc = info->app_info[app_index].app_id; | |
779 | // Calculate new checksum | |
780 | ppseudo_hdr->checksum = *pmsg++; | |
781 | //DEBUG("checksum = 0x%x\n", ppseudo_hdr->checksum); | |
782 | for (i=1; i<7; i++) { | |
783 | ppseudo_hdr->checksum ^= *pmsg++; | |
784 | //DEBUG("checksum = 0x%x\n", ppseudo_hdr->checksum); | |
785 | } | |
786 | pmsg++; | |
b13e39b2 | 787 | ppseudo_hdr = (struct pseudo_hdr *)pmsg; |
f7c1be0c | 788 | #if 0 |
7cfd8a37 | 789 | ptr = dpram_data; |
f7c1be0c MB |
790 | DEBUG("FT1000:ft1000_ChIoctl: Command Send\n"); |
791 | for (i=0; i<total_len; i++) { | |
792 | DEBUG("FT1000:ft1000_ChIoctl: data %d = 0x%x\n", i, *ptr++); | |
793 | } | |
794 | #endif | |
795 | //dpram_command.extra = 0; | |
f7c1be0c | 796 | |
bf3146c8 | 797 | //CardSendCommand(ft1000dev,(unsigned char*)&dpram_command,total_len+2); |
7cfd8a37 | 798 | CardSendCommand(ft1000dev,(unsigned short*)dpram_data,total_len+2); |
bf3146c8 GKH |
799 | |
800 | ||
f7c1be0c | 801 | info->app_info[app_index].nTxMsg++; |
f7c1be0c MB |
802 | } |
803 | else { | |
804 | result = -EINVAL; | |
f7c1be0c MB |
805 | } |
806 | } | |
807 | } | |
808 | } | |
809 | else { | |
810 | DEBUG("FT1000:ft1000_ChIoctl: Card not ready take messages\n"); | |
811 | result = -EACCES; | |
812 | } | |
7cfd8a37 | 813 | kfree(dpram_data); |
f7c1be0c MB |
814 | |
815 | } | |
816 | break; | |
817 | case IOCTL_GET_DPRAM_CMD: | |
818 | { | |
29437ab0 | 819 | struct dpram_blk *pdpram_blk; |
7cfd8a37 | 820 | IOCTL_DPRAM_BLK __user *pioctl_dpram; |
f7c1be0c MB |
821 | int msglen; |
822 | ||
823 | //DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_GET_DPRAM called\n"); | |
824 | ||
825 | if (ft1000_flarion_cnt == 0) { | |
826 | return (-EBADF); | |
827 | } | |
828 | ||
829 | // Search for matching file object | |
830 | for (i=0; i<MAX_NUM_APP; i++) { | |
7cfd8a37 | 831 | if ( info->app_info[i].fileobject == &File->f_owner) { |
f7c1be0c MB |
832 | //DEBUG("FT1000:ft1000_ChIoctl: Message is for AppId = %d\n", info->app_info[i].app_id); |
833 | break; | |
834 | } | |
835 | } | |
836 | ||
837 | // Could not find application info block | |
838 | if (i == MAX_NUM_APP) { | |
839 | DEBUG("FT1000:ft1000_ChIoctl:Could not find application info block\n"); | |
840 | result = -EBADF; | |
841 | break; | |
842 | } | |
843 | ||
844 | result = 0; | |
2a953cfd | 845 | pioctl_dpram = argp; |
f7c1be0c | 846 | if (list_empty(&info->app_info[i].app_sqlist) == 0) { |
bf3146c8 | 847 | //DEBUG("FT1000:ft1000_ChIoctl:Message detected in slow queue\n"); |
f7c1be0c | 848 | spin_lock_irqsave(&free_buff_lock, flags); |
29437ab0 | 849 | pdpram_blk = list_entry(info->app_info[i].app_sqlist.next, struct dpram_blk, list); |
f7c1be0c MB |
850 | list_del(&pdpram_blk->list); |
851 | info->app_info[i].NumOfMsg--; | |
852 | //DEBUG("FT1000:ft1000_ChIoctl:NumOfMsg for app %d = %d\n", i, info->app_info[i].NumOfMsg); | |
853 | spin_unlock_irqrestore(&free_buff_lock, flags); | |
854 | msglen = ntohs(*(u16 *)pdpram_blk->pbuffer) + PSEUDOSZ; | |
2a953cfd AB |
855 | result = get_user(msglen, &pioctl_dpram->total_len); |
856 | if (result) | |
857 | break; | |
858 | msglen = htons(msglen); | |
f7c1be0c MB |
859 | //DEBUG("FT1000:ft1000_ChIoctl:msg length = %x\n", msglen); |
860 | if(copy_to_user (&pioctl_dpram->pseudohdr, pdpram_blk->pbuffer, msglen)) | |
861 | { | |
862 | DEBUG("FT1000:ft1000_ChIoctl: copy fault occurred\n"); | |
863 | result = -EFAULT; | |
864 | break; | |
865 | } | |
866 | ||
867 | ft1000_free_buffer(pdpram_blk, &freercvpool); | |
868 | result = msglen; | |
869 | } | |
870 | //DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_GET_DPRAM no message\n"); | |
871 | } | |
872 | break; | |
873 | ||
874 | default: | |
875 | DEBUG("FT1000:ft1000_ChIoctl:unknown command: 0x%x\n", Command); | |
876 | result = -ENOTTY; | |
877 | break; | |
878 | } | |
879 | info->fAppMsgPend = 0; | |
880 | return result; | |
881 | } | |
882 | ||
883 | //--------------------------------------------------------------------------- | |
884 | // Function: ft1000_ChRelease | |
885 | // | |
bf3146c8 | 886 | // Parameters: |
f7c1be0c | 887 | // |
bf3146c8 | 888 | // Description: |
f7c1be0c MB |
889 | // |
890 | // Notes: | |
891 | // | |
892 | //--------------------------------------------------------------------------- | |
893 | static int ft1000_ChRelease (struct inode *Inode, struct file *File) | |
894 | { | |
1a88a068 | 895 | struct ft1000_info *info; |
f7c1be0c MB |
896 | struct net_device *dev; |
897 | int i; | |
29437ab0 | 898 | struct dpram_blk *pdpram_blk; |
bf3146c8 | 899 | |
f7c1be0c MB |
900 | DEBUG("ft1000_ChRelease called\n"); |
901 | ||
902 | dev = File->private_data; | |
1a88a068 | 903 | info = (struct ft1000_info *) netdev_priv(dev); |
f7c1be0c MB |
904 | |
905 | if (ft1000_flarion_cnt == 0) { | |
906 | info->appcnt--; | |
907 | return (-EBADF); | |
908 | } | |
909 | ||
910 | // Search for matching file object | |
911 | for (i=0; i<MAX_NUM_APP; i++) { | |
7cfd8a37 | 912 | if ( info->app_info[i].fileobject == &File->f_owner) { |
f7c1be0c MB |
913 | //DEBUG("FT1000:ft1000_ChIoctl: Message is for AppId = %d\n", info->app_info[i].app_id); |
914 | break; | |
915 | } | |
916 | } | |
917 | ||
918 | if (i==MAX_NUM_APP) | |
919 | return 0; | |
920 | ||
921 | while (list_empty(&info->app_info[i].app_sqlist) == 0) { | |
922 | DEBUG("Remove and free memory queue up on slow queue\n"); | |
29437ab0 | 923 | pdpram_blk = list_entry(info->app_info[i].app_sqlist.next, struct dpram_blk, list); |
f7c1be0c MB |
924 | list_del(&pdpram_blk->list); |
925 | ft1000_free_buffer(pdpram_blk, &freercvpool); | |
926 | } | |
bf3146c8 GKH |
927 | |
928 | // initialize application information | |
f7c1be0c MB |
929 | info->appcnt--; |
930 | DEBUG("ft1000_chdev:%s:appcnt = %d\n", __FUNCTION__, info->appcnt); | |
2a953cfd | 931 | info->app_info[i].fileobject = NULL; |
bf3146c8 | 932 | |
f7c1be0c MB |
933 | return 0; |
934 | } | |
935 |