cant figure out this PCI driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi guys.

Im first going to explain why im doing this and what I want to achieve before giving the problem.
My main objective here is to gain access to a piece of shared memory. The memory is shared between an FPGA and a PCI chip called a PLX9054.
I have to work through the PCI chip to gain access to the RAM.The only way I know of In order to do that is to build a driver PCI for Linux (Lubuntu).

Now Ive looked at a couple of examples and I have taken one of the examples and changed it quite allot. It seems like I am attaching to the rite device because when I check the VendorId and DeviceID they seem to be the right ones.

For now im debugging in the .write() function just to see if Im heading in the right direction at least "Latching on to RAM".Im doing that because now I can see all my functions whenever I use " echo 'write' > pciproto " where pciproto is the DEVICE file in /dev/ .

But I am not having any luck though. Ive been reading through /linux/pci.h   ,  /source/driver/pci/pci.c   , /source/driver/dma/dma.c  and allot of other source files and headers looking for something that can help me but what I have found doesn't seem to work. Ive tried DMA but checking DMA capability with pci_set_dma_mask() returned a FAIL as well as pci_request_region().

Im not quite sure what im suppose to use or to do to just be able to latch onto the piece of RAM.
If im going towards this all wrong please let me know and also let me know if theres any better way to do this.

Here is my CODE:

[CODE]

  1. /**
  2.  * This program is an attempt to latch on to the FG memory that is shared with the FPGA
  3.  * After a data sample has been taken the fg stores the data in this region.
  4.  * This Driver will latch on to the DEV file named pciproto. For testing purposes the /dev/pciproto has to be
  5.  *created manually in the /dev folder. The following command will create a DEV file with a maximum number of
  6.  *250 and a minor number of 0: mknod pciproto c 250 0
  7.  *
  8.  * There are only 3 registers:
  9.  * STATUS: addr = BAR2 + 0
  10.  *      unused for now
  11.  * READ_DATA: addr = BAR2 + 1
  12.  *      this register can only be read. Each time it's read, the device
  13.  *      read a byte from his input and return it.
  14.  * WRITE_DATA: addr = BAR2 + 2
  15.  *      this register can only be written. Each time it's written, the device
  16.  *      write the byte on his output.
  17.  *
  18.  */
  19.  
  20. #include <linux/kernel.h>
  21. #include <linux/init.h>
  22. #include <linux/module.h>
  23. #include <linux/pci.h>
  24. #include <linux/fs.h>
  25. #include <linux/cdev.h>         /* for cdev_ */
  26. #include <asm/uaccess.h>        /* for put_user */
  27. #include <linux/ioport.h>
  28.  
  29. #define MAX_DEVICE              8
  30. #define DEVICE_NAME             "pciproto"
  31. #define BAR_IO                  2
  32. #define BAR_MEM                 3
  33. #define BAR_SETTINGS            2
  34. #define BAR_PHOTO_MEM           3
  35.  
  36.  
  37. MODULE_DESCRIPTION("Test PCI driver");
  38. MODULE_AUTHOR("Gerfg ");
  39. MODULE_LICENSE("GPL");
  40.  
  41. /**
  42.  * This table holds the list of (VendorID,DeviceID) supported by this driver
  43.  *
  44.  */
  45. static struct pci_device_id pci_ids[] = {
  46.         { PCI_DEVICE(0x10B5, 0x9054), },
  47.         { 0, }
  48. };
  49.  
  50. /**
  51.  * This macro ...
  52.  *
  53.  */
  54. MODULE_DEVICE_TABLE(pci, pci_ids);
  55.  
  56. static dev_t devno;
  57. static int major;
  58.  
  59. /**
  60.  *  This structure is used to link a pci_dev to a cdev
  61.  *
  62.  */
  63. struct pci_cdev {
  64.         int minor;
  65.         struct pci_dev *pci_dev;
  66.         struct cdev *cdev;
  67. };
  68.  
  69. static struct pci_cdev pci_cdev[MAX_DEVICE];
  70.  
  71. /* this function initialize the table with all struct pci_cdev */
  72. static void pci_cdev_init(struct pci_cdev pci_cdev[], int size, int first_minor)
  73. {
  74.         int i;
  75.  
  76.         for(i=0; i<size; i++) {
  77.                 pci_cdev[i].minor   = first_minor++;
  78.                 pci_cdev[i].pci_dev = NULL;
  79.                 pci_cdev[i].cdev    = NULL;
  80.         printk(KERN_INFO "RAN: pci_dev_init(-pci_cdev-,%i,%i)\n",size,pci_cdev[i].minor);
  81.         }
  82. }
  83.  
  84. /*
  85.         -1      => failed
  86.          others => succes
  87. */
  88. static int pci_cdev_add(struct pci_cdev pci_cdev[], int size, struct pci_dev *pdev)
  89. {
  90.         int i, res = -1;
  91.  
  92.         for(i=0; i<size; i++) {
  93.                 if (pci_cdev[i].pci_dev == NULL) {
  94.                         pci_cdev[i].pci_dev = pdev;
  95.                         res = pci_cdev[i].minor;
  96.                         printk(KERN_INFO "RAN: pci_cdev_add(-pci_cdev-,%i,-dev-)\n",size);
  97.                         break;
  98.                 }
  99.         }
  100.        
  101.         return res;
  102. }
  103.  
  104. static void pci_cdev_del(struct pci_cdev pci_cdev[], int size, struct pci_dev *pdev)
  105. {
  106.         int i;
  107.  
  108.         for(i=0; i<size; i++) {
  109.                 if (pci_cdev[i].pci_dev == pdev) {
  110.                         pci_cdev[i].pci_dev = NULL;
  111.                 printk(KERN_INFO "RAN: pci_cdev_del(pci_cdevm,%i,dev)\n",size);
  112.                 }
  113.         }
  114. }
  115.  
  116. static struct pci_dev *pci_cdev_search_pci_dev(struct pci_cdev pci_cdev[], int size, int minor)
  117. {
  118.         int i;
  119.         struct pci_dev *pdev = NULL;
  120.  
  121.         for(i=0; i<size; i++) {
  122.                 if (pci_cdev[i].minor == minor) {
  123.                         pdev = pci_cdev[i].pci_dev;
  124.                         break;
  125.                 printk(KERN_INFO "RAN: pci_cdev_search_pci_dev()\n");
  126.                 }
  127.         }
  128.  
  129.         return pdev;   
  130. }
  131.  
  132. static struct cdev *pci_cdev_search_cdev(struct pci_cdev pci_cdev[], int size, int minor)
  133. {
  134.         int i;
  135.         struct cdev *cdev = NULL;
  136.  
  137.         for(i=0; i<size; i++) {
  138.                 if (pci_cdev[i].minor == minor) {
  139.                         cdev = pci_cdev[i].cdev;
  140.                         break;
  141.                 }
  142.         }
  143.         printk(KERN_INFO "RAN: pci_cdev_search_cdev()\n");
  144.         return cdev;   
  145. }
  146.  
  147. /*
  148.         -1      => not found
  149.         others  => found
  150. */
  151. static int pci_cdev_search_minor(struct pci_cdev pci_cdev[],
  152.                 int size, struct pci_dev *pdev)
  153. {
  154.         int i, minor = -1;
  155.  
  156.         for(i=0; i<size; i++) {
  157.                 if (pci_cdev[i].pci_dev == pdev) {
  158.                         minor = pci_cdev[i].minor;
  159.                         break;
  160.                 }
  161.         }
  162.         printk(KERN_INFO "RAN: pci_cdev_search_minor()\n");
  163.         return minor;
  164. }
  165.  
  166.  
  167.  
  168. /**
  169.  * This function is called when the device node is opened
  170.  *
  171.  */
  172. static int pci_open(struct inode *inode, struct file *file)
  173. {
  174.         int minor = iminor(inode);
  175.         file->private_data = (void *)pci_cdev_search_pci_dev(pci_cdev, MAX_DEVICE, minor);
  176.         printk(KERN_INFO "RAN: pci_open()\n"); 
  177.         return 0;
  178. }
  179.  
  180. /**
  181.  * This function is called when the device node is closed
  182.  *
  183.  */
  184. static int pci_release(struct inode *inode, struct file *file)
  185. {
  186.         return 0;
  187. }
  188.  
  189. /**
  190.  * This function is called when the device node is read
  191.  *
  192.  */
  193. static ssize_t pci_read(struct file *file,      /* see include/linux/fs.h   */
  194.                            char *buffer,        /* buffer to fill with data */
  195.                            size_t length,       /* length of the buffer     */
  196.                            loff_t * offset)
  197. {
  198.         int byte_read = 0;
  199.         unsigned char value;
  200.         struct pci_dev *pdev = (struct pci_dev *)file->private_data;
  201.         unsigned long pci_io_addr = 0;
  202.  
  203.         pci_io_addr = pci_resource_start(pdev,BAR_IO);
  204.  
  205.         while (byte_read < length) {
  206.                 /* read a byte from the input */
  207.                 value = inb(pci_io_addr + 1);
  208.  
  209.                 /* write the value in the user buffer */
  210.                 put_user(value, &buffer[byte_read]);
  211.  
  212.                 byte_read++;
  213.         }
  214.         printk(KERN_INFO "RAN: pci_read()\n");
  215.         return byte_read;
  216. }
  217.  
  218. /**
  219.  * This function is called when the device node is read
  220.  *
  221.  */
  222. static ssize_t pci_write(struct file *filp, const char *buffer, size_t len, loff_t * off) {
  223.         int i;
  224.         unsigned char value;
  225.         struct pci_dev *pdev = (struct pci_dev *)filp->private_data;
  226.         unsigned long pci_reg_mem_addr = 0lu;
  227.         unsigned long pci_photo_mem_addr = 0lu;
  228.         unsigned int dma_mask = 1;
  229.         int test=2;
  230.         const char *MemRegionName = NULL;
  231.         //uint32_t *NEW;
  232.         //MemRegionName = (const char*)malloc(sizeof(const char));
  233.         printk(KERN_INFO "RAN: pci_write()\n");
  234.         pci_reg_mem_addr = pci_resource_start(pdev,BAR_SETTINGS);
  235.         pci_photo_mem_addr = pci_resource_start(pdev,BAR_PHOTO_MEM);
  236.         printk(KERN_INFO "Device VendorID = %x , DeviceID = %x",pdev->vendor,pdev->device);
  237.         printk(KERN_INFO "DATA WRITTEN TO DEVICE:");
  238.         for(i=0; i<len; i++) {
  239.                 /* read value on the buffer */
  240.                 value = (unsigned char)buffer[i];
  241.                 printk(KERN_INFO "%c",value);
  242.                 /* write data to the device */
  243.                 outb(pci_reg_mem_addr+2, value);
  244.                
  245.         }
  246.  
  247.  
  248.         if ((test=(pci_set_dma_mask(pdev,dma_mask)))<0)
  249.         {
  250.         printk(KERN_INFO "PCI device failed DMA support test returned %u \n",test);
  251.         }
  252.         else
  253.         {
  254.         printk(KERN_INFO "PCI Dev supports DMA\n");    
  255.         }
  256.        
  257.  
  258.        
  259.         if ((test=(pci_request_region(pdev,BAR_PHOTO_MEM,MemRegionName)))!=0)
  260.         {
  261.         printk(KERN_INFO "request region failed: returned %u \n",test);
  262.         }
  263.         else
  264.         {
  265.         printk(KERN_INFO "Request region success!\n"); 
  266.         }      
  267.        
  268.         //NEW = (uint32_t *)mmap(0,0x7fffff,MAP_SHARED,0,"/dev/FGpci",BAR_PHOTO_MEM);
  269.        
  270.         test = check_mem_region(pci_photo_mem_addr,0x7ffffe);
  271.         printk(KERN_INFO "Region check returned %x \n",test);
  272.         outb(pci_reg_mem_addr+4, 0b00000111);
  273.         outb(pci_reg_mem_addr+5, 0b00000001);
  274.         //prototype pci_resource_start(*pdev,BAR);
  275.         printk("BAR_PHOTO_MEM Starts at 0x%x and len is 0x%x",pci_resource_start(pdev,BAR_PHOTO_MEM),pci_resource_len(pdev,BAR_PHOTO_MEM));
  276.        
  277.         printk(KERN_INFO "\n");
  278.         printk(KERN_INFO "CHILD: pci_reg_mem_addr = %lx \n",pci_reg_mem_addr);
  279.         printk(KERN_INFO "CHILD: pci_photo_mem_addr = %lx \n",pci_photo_mem_addr);
  280.         printk(KERN_INFO "CHILD: pci_reg_mem_addr+4 = %lx || pci_reg_mem_addr+5 = %lx \n",
  281.                                                         (pci_reg_mem_addr+4),(pci_reg_mem_addr+5));
  282.         outb(pci_reg_mem_addr+5, 0b00000000);
  283.         return len;
  284. }
  285.  
  286. /**
  287.  * This structure holds informations about the pci node
  288.  *
  289.  */
  290. static struct file_operations pci_ops = {
  291.         .owner          = THIS_MODULE,
  292.         .read           = pci_read,
  293.         .write          = pci_write,
  294.         .open           = pci_open,
  295.         .release        = pci_release
  296. };
  297.  
  298.  
  299. /**
  300.  * This function is called when a new pci device is associated with a driver
  301.  *
  302.  * return: 0 => this driver don't handle this device
  303.  *         1 => this driver handle this device
  304.  *
  305.  */
  306. static int pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
  307. {
  308.         int ret, minor;
  309.         struct cdev *cdev;
  310.         dev_t devno;
  311.  
  312.         /* add this pci device in pci_cdev */
  313.         if ((minor = pci_cdev_add(pci_cdev, MAX_DEVICE, dev)) < 0)
  314.                 goto error;
  315.  
  316.         /* compute major/minor number */
  317.         devno = MKDEV(major, minor);
  318.  
  319.         /* allocate struct cdev */
  320.         cdev = cdev_alloc();
  321.  
  322.         /* initialise struct cdev */
  323.         cdev_init(cdev, &pci_ops);
  324.         cdev->owner = THIS_MODULE;
  325.  
  326.         /* register cdev */
  327.         ret = cdev_add(cdev, devno, 1);
  328.         if (ret < 0) {
  329.                 dev_err(&(dev->dev), "Can't register character device\n");
  330.                 goto error;
  331.         }
  332.         pci_cdev[minor].cdev = cdev;
  333.  
  334.         dev_info(&(dev->dev), "%s The major device number is %d (%d).\n",
  335.                "Registeration is a success", MAJOR(devno), MINOR(devno));
  336.         dev_info(&(dev->dev), "If you want to talk to the device driver,\n");
  337.         dev_info(&(dev->dev), "you'll have to create a device file. \n");
  338.         dev_info(&(dev->dev), "We suggest you use:\n");
  339.         dev_info(&(dev->dev), "mknod %s c %d %d\n", DEVICE_NAME, MAJOR(devno), MINOR(devno));
  340.         dev_info(&(dev->dev), "The device file name is important, because\n");
  341.         dev_info(&(dev->dev), "the ioctl program assumes that's the\n");
  342.         dev_info(&(dev->dev), "file you'll use.\n");
  343.  
  344.         /* enable the device */
  345.         pci_enable_device(dev);
  346.  
  347.         /* 'alloc' IO to talk with the card */
  348.         if (pci_request_region(dev, BAR_PHOTO_MEM, "MEM-pci") == 0) {
  349.                 dev_err(&(dev->dev), "Can't request BAR2\n");
  350.                 cdev_del(cdev);
  351.                 goto error;
  352.         }
  353.  
  354.         /* check that BAR_IO is *really* IO region */
  355.         if ((pci_resource_flags(dev, BAR_PHOTO_MEM) & IORESOURCE_MEM) != IORESOURCE_MEM) {
  356.                 dev_err(&(dev->dev), "BAR2 isn't an IO region\n");
  357.                 cdev_del(cdev);
  358.                 goto error;
  359.         }
  360.        
  361.  
  362.         return 1;
  363.  
  364. error:
  365.         return 0;
  366. }
  367.  
  368. /**
  369.  * This function is called when the driver is removed
  370.  *
  371.  */
  372. static void pci_remove(struct pci_dev *dev)
  373. {
  374.         int minor;
  375.         struct cdev *cdev;
  376.  
  377.         /* remove associated cdev */
  378.         minor = pci_cdev_search_minor(pci_cdev, MAX_DEVICE, dev);
  379.         cdev = pci_cdev_search_cdev(pci_cdev, MAX_DEVICE, minor);
  380.         if (cdev != NULL)
  381.                 cdev_del(cdev);
  382.                
  383.         /* remove this device from pci_cdev */
  384.         pci_cdev_del(pci_cdev, MAX_DEVICE, dev);
  385.  
  386.         /* release the IO region */
  387.         pci_release_region(dev, BAR_IO);
  388. }
  389.  
  390. /**
  391.  * This structure holds informations about the pci driver
  392.  *
  393.  */
  394. static struct pci_driver pci_driver = {
  395.         .name           = "pci",
  396.         .id_table       = pci_ids,
  397.         .probe          = pci_probe,
  398.         .remove         = pci_remove,
  399. };
  400.  
  401.  
  402. /**
  403.  * This function is called when the module is loaded
  404.  *
  405.  */
  406. static int __init pci_init_module(void)
  407. {
  408.         int ret;
  409.  
  410.         printk(KERN_DEBUG "Module pci init\n");
  411.  
  412.         /* allocate (several) major number */
  413.         ret = alloc_chrdev_region(&devno, 0, MAX_DEVICE, "buffer");
  414.         if (ret < 0) {
  415.                 printk(KERN_ERR "Can't get major\n");
  416.                 return ret;
  417.         }
  418.  
  419.         /* get major number and save it in major */
  420.         major = MAJOR(devno);
  421.  
  422.         /* initialise pci_cdev */
  423.         pci_cdev_init(pci_cdev, MAX_DEVICE, MINOR(devno));
  424.  
  425.         /* register pci driver */
  426.         ret = pci_register_driver(&pci_driver);
  427.         if (ret < 0) {
  428.                 /* free major/minor number */
  429.                 unregister_chrdev_region(devno, 1);
  430.  
  431.                 printk(KERN_ERR "pci-driver: can't register pci driver\n");
  432.                 return ret;
  433.         }
  434.  
  435.  
  436.         return 0;
  437. }
  438.  
  439. /**
  440.  * This function is called when the module is unloaded
  441.  *
  442.  */
  443. static void pci_exit_module(void)
  444. {
  445.         int i;
  446.  
  447.         /* unregister pci driver */
  448.         pci_unregister_driver(&pci_driver);
  449.  
  450.         /* unregister character device */
  451.         for(i=0; i< MAX_DEVICE; i++) {
  452.                 if (pci_cdev[i].pci_dev != NULL) {
  453.                         cdev_del(pci_cdev[i].cdev);
  454.                 }
  455.         }
  456.  
  457.         /* free major/minor number */
  458.         unregister_chrdev_region(devno, MAX_DEVICE);
  459.  
  460.         printk(KERN_DEBUG "Module pci exit\n");
  461. }
  462.  
  463. module_init(pci_init_module);
  464. module_exit(pci_exit_module);


[/CODE]

Here is my output on " tail -f /var/log/kern.log "

  1. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879644] Module pci init
  2. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879659] RAN: pci_dev_init(-pci_cdev-,8,0)
  3. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879663] RAN: pci_dev_init(-pci_cdev-,8,1)
  4. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879667] RAN: pci_dev_init(-pci_cdev-,8,2)
  5. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879670] RAN: pci_dev_init(-pci_cdev-,8,3)
  6. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879673] RAN: pci_dev_init(-pci_cdev-,8,4)
  7. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879677] RAN: pci_dev_init(-pci_cdev-,8,5)
  8. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879680] RAN: pci_dev_init(-pci_cdev-,8,6)
  9. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879683] RAN: pci_dev_init(-pci_cdev-,8,7)
  10. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879742] RAN: pci_cdev_add(-pci_cdev-,8,-dev-)
  11. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879754] pci 0000:02:05.0: Registeration is a success The major device number is 250 (0).
  12. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879760] pci 0000:02:05.0: If you want to talk to the device driver,
  13. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879765] pci 0000:02:05.0: you'll have to create a device file.
  14. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879769] pci 0000:02:05.0: We suggest you use:
  15. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879775] pci 0000:02:05.0: mknod FGpci c 250 0
  16. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879780] pci 0000:02:05.0: The device file name is important, because
  17. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879785] pci 0000:02:05.0: the ioctl program assumes that's the
  18. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879790] pci 0000:02:05.0: file you'll use.
  19. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879807] pci 0000:02:05.0: BAR 3: can't reserve [mem 0xe0800000-0xe0ffffff]
  20. Dec 10 09:00:49 dcam-desktop kernel: [ 2228.879813] pci 0000:02:05.0: Driver probe function unexpectedly returned 1
  21. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073319] RAN: pci_open()
  22. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073360] RAN: pci_write()
  23. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073365] Device VendorID = 10b5 , DeviceID = 9054
  24. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073368] DATA WRITTEN TO DEVICE:w
  25. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073374] ri
  26. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073381] te
  27. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073387]
  28. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073392] PCI device failed DMA support test returned 4294967291
  29. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073411] pci 0000:02:05.0: BAR 3: can't reserve [mem 0xe0800000-0xe0ffffff]
  30. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073415] request region failed: returned 4294967280
  31. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073419] Region check returned fffffff0
  32. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073425] BAR_PHOTO_MEM Starts at 0xe0800000 and len is 0x0
  33. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073427]
  34. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073431] CHILD: pci_reg_mem_addr = e0201000
  35. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073434] CHILD: pci_photo_mem_addr = e0800000
  36. Dec 10 09:00:53 dcam-desktop kernel: [ 2233.073438] CHILD: pci_reg_mem_addr+4 = e0201004 || pci_reg_mem_addr+5 = e0201005
  37. Dec 10 09:20:35 dcam-desktop kernel: [ 3414.852735] e100 0000:02:08.0 eth0: NIC Link is Up 100 Mbps Full Duplex


_______________________________________________
Kernelnewbies mailing list
Kernelnewbies@xxxxxxxxxxxxxxxxx
http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies

[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux