CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
orangepi-xunlong

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: orangepi-xunlong/orangepi-build
Path: blob/next/external/cache/sources/linuxpg/pgdrv.c
Views: 3959
1
#include <linux/version.h>
2
#include <linux/module.h>
3
#include <linux/cdev.h>
4
#include <linux/kernel.h>
5
#include <linux/delay.h>
6
#include <linux/interrupt.h>
7
#include <linux/pci.h>
8
#include <linux/fs.h> // struct file_operations
9
#include <linux/mm.h> // mmap
10
#include <linux/slab.h> // kmalloc
11
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
12
#include <linux/pci-aspm.h>
13
#endif
14
#include <asm/io.h>
15
#include <asm/uaccess.h> // copy_to_user
16
#include <linux/uaccess.h> // copy_to_user
17
#include <asm/byteorder.h>
18
19
#include "pgdrv.h"
20
21
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
22
#warning KERNEL_VERSION < 2.6.11
23
#endif
24
25
static DEV_INFO dev_info[MAX_DEV_NUM];
26
static atomic_t dev_num;
27
static spinlock_t module_lock;
28
static int major = 0;
29
static unsigned long mmio = 0;
30
31
module_param(major, int, S_IRUGO|S_IWUSR);
32
module_param(mmio, ulong, S_IRUGO|S_IWUSR);
33
34
//static int dev_IoctlFun(struct inode *inode, struct file *pFile,unsigned int cmd, unsigned long arg)
35
static long dev_IoctlFun(struct file *pFile,unsigned int cmd, unsigned long arg)
36
{
37
PPGDEV mydev;
38
int result;
39
40
if (_IOC_TYPE(cmd) != RTL_IOC_MAGIC)
41
{
42
DbgFunPrint("Invalid command!!!");
43
return -ENOTTY;
44
}
45
46
mydev = (PPGDEV)pFile->private_data;
47
result = 0;
48
49
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
50
if (down_interruptible(&mydev->dev_sem))
51
{
52
DbgFunPrint("lock fail");
53
return -ERESTARTSYS;
54
}
55
#else
56
if (mutex_lock_interruptible(&mydev->dev_mutex))
57
{
58
DbgFunPrint("lock fail");
59
return -ERESTARTSYS;
60
}
61
#endif
62
63
switch(cmd)
64
{
65
case IOC_PCI_CONFIG:
66
{
67
PCI_CONFIG_RW pci_config;
68
69
if (copy_from_user(&pci_config, (void __user *)arg, sizeof(PCI_CONFIG_RW)))
70
{
71
DbgFunPrint("copy_from_user fail");
72
result = -EFAULT;
73
break;
74
}
75
76
77
if (pci_config.bRead==TRUE)
78
{
79
if (pci_config.size==1)
80
{
81
result = pci_read_config_byte(mydev->pdev, pci_config.addr, &pci_config.byte);
82
}
83
else if (pci_config.size==2)
84
{
85
result = pci_read_config_word(mydev->pdev, pci_config.addr, &pci_config.word);
86
}
87
else if (pci_config.size==4)
88
{
89
result = pci_read_config_dword(mydev->pdev, pci_config.addr, &pci_config.dword);
90
}
91
else
92
{
93
result = -EINVAL;
94
}
95
96
if (result)
97
{
98
DbgFunPrint("Read PCI fail:%d",result);
99
break;
100
}
101
102
if (copy_to_user((void __user *)arg , &pci_config, sizeof(PCI_CONFIG_RW)))
103
{
104
DbgFunPrint("copy_from_user fail");
105
result = -EFAULT;
106
break;
107
}
108
}
109
else // write
110
{
111
if (pci_config.size==1)
112
{
113
result = pci_write_config_byte(mydev->pdev, pci_config.addr, pci_config.byte);
114
}
115
else if (pci_config.size==2)
116
{
117
result = pci_write_config_word(mydev->pdev, pci_config.addr, pci_config.word);
118
}
119
else if (pci_config.size==4)
120
{
121
result = pci_write_config_dword(mydev->pdev, pci_config.addr, pci_config.dword);
122
}
123
else
124
{
125
result = -EINVAL;
126
}
127
128
if (result)
129
{
130
DbgFunPrint("Write PCI fail:%d",result);
131
break;
132
}
133
}
134
#if 0
135
DebugPrint("bRead=%u, size=%d, addr=0x%02x , data=0x%08x",
136
pci_config.bRead, pci_config.size,
137
pci_config.addr, pci_config.dword);
138
#endif
139
break;
140
}
141
142
case IOC_IOMEM_OFFSET:
143
if ( put_user(mydev->offset,(unsigned int __user *)arg) )
144
{
145
DbgFunPrint("put_user fail");
146
result = -EFAULT;
147
break;
148
}
149
break;
150
151
case IOC_DEV_FUN:
152
if ( put_user(mydev->pdev->devfn,(unsigned int __user *)arg) )
153
{
154
DbgFunPrint("put_user fail");
155
result = -EFAULT;
156
break;
157
}
158
break;
159
160
default:
161
DbgFunPrint("Command not support!!!");
162
result = -ENOTTY;
163
break;
164
}
165
166
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
167
up(&mydev->dev_sem);
168
#else
169
mutex_unlock(&mydev->dev_mutex);
170
#endif
171
return result;
172
}
173
174
ssize_t dev_read(struct file *filp, char __user *buffer, size_t count, loff_t *f_pos)
175
{
176
PPGDEV mydev;
177
BYTE *buf;
178
ssize_t readcount;
179
180
DbgFunPrint("count=%zd",count);
181
mydev = (PPGDEV)filp->private_data;
182
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
183
if (down_interruptible(&mydev->dev_sem))
184
{
185
DbgFunPrint("lock fail");
186
return 0;
187
}
188
#else
189
if (mutex_lock_interruptible(&mydev->dev_mutex))
190
{
191
DbgFunPrint("lock fail");
192
return 0;
193
}
194
#endif
195
196
// To Do:
197
if (count > 0x100)
198
{
199
count = 0x100;
200
// return 0;
201
}
202
else
203
{
204
count &= ~0x3;
205
}
206
buf = kmalloc(count,GFP_KERNEL);
207
if (!buf)
208
{
209
return -ENOMEM;
210
}
211
212
for (readcount=0;readcount<count;readcount+=4)
213
{
214
if (pci_read_config_dword(mydev->pdev,readcount,(u32 *)&buf[readcount]))
215
{
216
break;
217
}
218
}
219
220
if (copy_to_user(buffer,buf,readcount))
221
{
222
readcount = 0;
223
}
224
*f_pos += readcount;
225
226
kfree(buf);
227
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
228
up(&mydev->dev_sem);
229
#else
230
mutex_unlock(&mydev->dev_mutex);
231
#endif
232
return readcount;
233
}
234
235
static int dev_Open(struct inode *inode, struct file *pFile)
236
{
237
PPGDEV mydev;
238
239
mydev = container_of(inode->i_cdev, PGDEV, cdev);
240
DbgFunPrint("mydev=%p",mydev);
241
242
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
243
atomic_inc(&mydev->count);
244
if (atomic_read(&mydev->count) > 1)
245
{
246
atomic_dec(&mydev->count);
247
DbgFunPrint("Busy");
248
return -EMFILE;
249
}
250
#else
251
if (atomic_inc_return(&mydev->count) > 1)
252
{
253
atomic_dec(&mydev->count);
254
DbgFunPrint("Busy");
255
return -EMFILE;
256
}
257
#endif
258
259
pFile->private_data = mydev;
260
261
return 0;
262
}
263
264
static int dev_Close(struct inode *inode, struct file *pFile)
265
{
266
PPGDEV mydev;
267
268
mydev = container_of(inode->i_cdev, PGDEV, cdev);
269
DbgFunPrint("mydev=%p",mydev);
270
pFile->private_data = NULL;
271
272
atomic_dec(&mydev->count);
273
274
return 0;
275
}
276
277
static void dev_vma_open(struct vm_area_struct *vma)
278
{
279
DebugPrint("dev_vma_open");
280
// DbgFunPrint("vma=0x%08x, vm_start=0x%08x, vm_end=0x%08x, vm_pgoff=%d\n",
281
// (DWORD)vma, (DWORD)vma->vm_start, (DWORD)vma->vm_end,
282
// (DWORD)vma->vm_pgoff);
283
}
284
285
static void dev_vma_close(struct vm_area_struct *vma)
286
{
287
DebugPrint("dev_vma_close");
288
// DbgFunPrint("vma=0x%08x, vm_start=0x%08x, vm_end=0x%08x, vm_pgoff=%d\n",
289
// (DWORD)vma, (DWORD)vma->vm_start, (DWORD)vma->vm_end,
290
// (DWORD)vma->vm_pgoff);
291
}
292
293
static struct vm_operations_struct dev_vma_ops = {
294
.open = dev_vma_open,
295
.close = dev_vma_close,
296
};
297
298
static int dev_mmap(struct file *filp, struct vm_area_struct *vma)
299
{
300
PPGDEV mydev;
301
302
mydev = (PPGDEV)filp->private_data;
303
304
if (!mydev)
305
{
306
DbgFunPrint("NULL pointer");
307
return -EFAULT;
308
}
309
DbgFunPrint("\nvma=%p, vm_start=0x%08x, vm_end=0x%08x\nvm_pgoff=%d, vm_flags=0x%x, mydev=%p\n",
310
vma, (DWORD)vma->vm_start, (DWORD)vma->vm_end,
311
(DWORD)vma->vm_pgoff,(DWORD)vma->vm_flags,
312
mydev);
313
314
if (!mydev || !mydev->base_phyaddr)
315
{
316
DbgFunPrint("Invalid base_phyaddr=0x%08x",(DWORD)mydev->base_phyaddr);
317
return -ENXIO;
318
}
319
320
switch(mydev->deviceID)
321
{
322
case 0x8167:
323
case 0x8169:
324
{
325
unsigned int iomem;
326
pci_read_config_dword(mydev->pdev, 0x14, &iomem);
327
if (!iomem)
328
{
329
DbgFunPrint("Invalid iomem=0x%08x, base_phyaddr=0x%08x",iomem,(DWORD)mydev->base_phyaddr);
330
pci_write_config_dword(mydev->pdev, 0x14, (DWORD)mydev->base_phyaddr);
331
}
332
break;
333
}
334
335
default:
336
break;
337
}
338
339
vma->vm_flags |= VM_IO;
340
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
341
if (remap_page_range(vma, vma->vm_start, mydev->base_phyaddr, vma->vm_end-vma->vm_start, vma->vm_page_prot))
342
{
343
DbgFunPrint("remap_page_range fail!!!");
344
return -EAGAIN;
345
}
346
#else
347
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
348
if (remap_pfn_range(vma, vma->vm_start, mydev->base_phyaddr>>PAGE_SHIFT, vma->vm_end-vma->vm_start, vma->vm_page_prot))
349
{
350
DbgFunPrint("remap_pfn_range fail!!!");
351
return -EAGAIN;
352
}
353
#endif
354
vma->vm_ops = &dev_vma_ops;
355
dev_vma_open(vma);
356
return 0;
357
}
358
359
static struct file_operations pg_fops = {
360
.owner = THIS_MODULE,
361
.read = dev_read,
362
// .write = NsmWriteFun,
363
.mmap = dev_mmap,
364
.unlocked_ioctl = dev_IoctlFun,
365
.open = dev_Open,
366
.release = dev_Close,
367
};
368
369
static int __devinit pgdrv_prob(struct pci_dev *pdev, const struct pci_device_id *id)
370
{
371
PPGDEV mydev;
372
dev_t devno;
373
int pg_minor, result, i;
374
375
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
376
pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
377
PCIE_LINK_STATE_CLKPM);
378
#endif
379
380
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
381
atomic_inc(&dev_num);
382
if (atomic_read(&dev_num) > MAX_DEV_NUM)
383
{
384
atomic_dec(&dev_num);
385
DbgFunPrint("Too Many Device");
386
return -EFAULT;
387
}
388
#else
389
if (atomic_inc_return(&dev_num) > MAX_DEV_NUM)
390
{
391
atomic_dec(&dev_num);
392
DbgFunPrint("Too Many Device");
393
return -EFAULT;
394
}
395
#endif
396
397
mydev = kmalloc(sizeof(PGDEV),GFP_KERNEL);
398
if (!mydev)
399
{
400
DebugPrint("Allocate dev fail");
401
return -ENOMEM;
402
}
403
memset(mydev, 0, sizeof(PGDEV));
404
405
pg_minor = -1;
406
devno = 0;
407
spin_lock(&module_lock);
408
for (i = 0; i < MAX_DEV_NUM; i++)
409
{
410
if (dev_info[i].bUsed == FALSE)
411
{
412
dev_info[i].bUsed = TRUE;
413
devno = dev_info[i].devno;
414
mydev->index = i;
415
break;
416
}
417
}
418
spin_unlock(&module_lock);
419
420
mydev->pdev = pdev;
421
atomic_set(&mydev->count, 0);
422
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
423
init_MUTEX(&mydev->dev_sem);
424
#else
425
mutex_init(&mydev->dev_mutex);
426
#endif
427
428
pg_minor = MINOR(devno);
429
DbgFunPrint("mydev=%p, major=%u, minor=%u",mydev,major,pg_minor);
430
431
cdev_init(&mydev->cdev, &pg_fops);
432
mydev->cdev.owner = THIS_MODULE;
433
result = cdev_add(&mydev->cdev, devno, 1);
434
if (result)
435
{
436
DebugPrint("cdev_add fault");
437
spin_lock(&module_lock);
438
dev_info[mydev->index].bUsed = FALSE;
439
spin_unlock(&module_lock);
440
kfree(mydev);
441
return result;
442
}
443
444
pci_set_drvdata(pdev, mydev);
445
446
result = pci_enable_device(mydev->pdev);
447
if (result<0)
448
{
449
DbgFunPrint("pci_enable_device fail");
450
return result;
451
}
452
453
result = pci_request_regions(mydev->pdev, MODULENAME);
454
if (result<0)
455
{
456
DbgFunPrint("pci_request_regions fail");
457
goto error_2;
458
}
459
460
pci_set_master(mydev->pdev);
461
462
if (mmio) {
463
mydev->base_phyaddr = mmio;
464
} else {
465
switch(id->device) {
466
case 0x8167:
467
case 0x8169:
468
mydev->base_phyaddr = pci_resource_start(mydev->pdev, 1);
469
break;
470
default:
471
mydev->base_phyaddr = pci_resource_start(mydev->pdev, 2);
472
break;
473
}
474
}
475
476
if (!mydev->base_phyaddr) {
477
DbgFunPrint("Invalid phyaddress");
478
result = -EFAULT;
479
goto error_1;
480
}
481
mydev->deviceID = id->device;
482
mydev->offset = mydev->base_phyaddr & ((1 << PAGE_SHIFT) - 1);
483
DbgFunPrint("ID=0x%04x base_phyaddr=0x%08x, offset=0x%08x",
484
mydev->deviceID , (DWORD)mydev->base_phyaddr,
485
mydev->offset);
486
487
return 0;
488
489
error_1:
490
pci_release_regions(mydev->pdev);
491
error_2:
492
pci_disable_device(mydev->pdev);
493
cdev_del(&mydev->cdev);
494
kfree(mydev);
495
return result;
496
}
497
498
static void __devexit pgdrv_remove(struct pci_dev *pdev)
499
{
500
PPGDEV mydev;
501
502
mydev = pci_get_drvdata(pdev);
503
DbgFunPrint("mydev=%p",mydev);
504
505
mydev->base_phyaddr = 0;
506
pci_release_regions(pdev);
507
pci_disable_device(pdev);
508
509
cdev_del(&mydev->cdev);
510
spin_lock(&module_lock);
511
dev_info[mydev->index].bUsed = FALSE;
512
spin_unlock(&module_lock);
513
kfree(mydev);
514
pci_set_drvdata(pdev, NULL);
515
atomic_dec(&dev_num);
516
}
517
518
static struct pci_device_id pgdrv_id_table[] = {
519
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8125), },
520
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8136), },
521
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8161), },
522
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8162), },
523
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), },
524
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), },
525
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), },
526
{0,},
527
};
528
529
MODULE_DEVICE_TABLE(pci, pgdrv_id_table);
530
531
static struct pci_driver pgdrv_pci_driver = {
532
.name = MODULENAME,
533
.id_table = pgdrv_id_table,
534
.probe = pgdrv_prob,
535
.remove = __devexit_p(pgdrv_remove),
536
};
537
538
static int __init pgdrv_init(void)
539
{
540
int result, i;
541
dev_t devno;
542
543
memset(dev_info, 0, sizeof(dev_info));
544
atomic_set(&dev_num,0);
545
spin_lock_init(&module_lock);
546
547
if (!major)
548
{
549
result = alloc_chrdev_region(&devno, 0, MAX_DEV_NUM, MODULENAME);
550
major = MAJOR(devno);
551
}
552
else
553
{
554
devno = MKDEV(major, 0);
555
result = register_chrdev_region(devno, MAX_DEV_NUM, MODULENAME);
556
}
557
558
if (result < 0)
559
{
560
DebugPrint("Can't get major %d", major);
561
return result;
562
}
563
DbgFunPrint("Major : %d",major);
564
565
for (i=0; i<MAX_DEV_NUM; i++)
566
{
567
dev_info[i].devno = MKDEV(major, i);
568
}
569
570
return pci_register_driver(&pgdrv_pci_driver);
571
}
572
573
static void __exit pgdrv_exit(void)
574
{
575
DbgFunPrint();
576
pci_unregister_driver(&pgdrv_pci_driver);
577
unregister_chrdev_region(MKDEV(major, 0), MAX_DEV_NUM);
578
}
579
580
module_init(pgdrv_init);
581
module_exit(pgdrv_exit);
582
583
MODULE_LICENSE("GPL");
584
MODULE_AUTHOR("Network Interface Controllers crew <[email protected]>");
585
MODULE_VERSION("1.00.00");
586
MODULE_DESCRIPTION("RealTek driver of Ethernet PG tool");
587
588