I'm trying to write my own Linux PCIe driver. I would like to write my mmap function so that it maps bar0, I realize I can do this without writing a driver but I'm doing this mostly for learning purposes.
My first question is why would you need to implement mmap if you can mmap bar0 without any driver development?
My second question is why is my mmap not working?
Here is my mmap code and the userspace app I use to access it
static int dma_proxy_mmap(struct file *filp, struct vm_area_struct *vma)
{
if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,vma->vm_end - vma->vm_start,vma->vm_page_prot))
{
return -EAGAIN;
}
printk(KERN_ALERT "mmap done\n");
return 0;
}
and here is my user space code
int main()
{
int proxy_fd;
int *MyMmap;
proxy_fd = open("/dev/scull", O_RDWR);
MyMmap = (int*)mmap(0,32, PROT_READ | PROT_WRITE, MAP_SHARED, proxy_fd, 0);
if (MyMmap == MAP_FAILED) {
printf("mmap failed");
}
MyMmap[0] = 10;
printf ("Decimals: %d\n", MyMmap[0]);
}
I know it's not working correctly because my pcie card is designed to write a different value regardless of what I send to it to write which I've verified is working by mmaping to resource0 of that board.
Related
I am using ‘/dev/nvme1’ as my data path, now I want to tell SSD i don’t need [offset, len] anymore, is there a posix API to do that? To send a trim command to SSD FTL.
After some research, I found a piece of code here:
int block_device_discard(int fd, int64_t offset, int64_t len)
{
uint64_t range[2] = {(uint64_t)offset, (uint64_t)len};
int ret = ioctl(fd, BLKSECDISCARD, range);
if (ret < 0) {
return errno;
}
return 0;
}
Note that if your device is not able to support BLKSECDISCARD, the command will return an error.
Not sure if it works yet, will update the result later.
References:
https://rwmj.wordpress.com/2014/03/11/blkdiscard-blkzeroout-blkdiscardzeroes-blksecdiscard/
how to TRIM a block on SSD disk?
https://www.man7.org/linux/man-pages/man2/ioctl.2.html
https://static.lwn.net/images/pdf/LDD3/ch06.pdf
Using this guide/class (http://playground.arduino.cc/Interfacing/CPPWindows) I wrote a little application to send data on the serial port to an Arduino. (This will be added on a larger project later on)
int _tmain(int argc, _TCHAR* argv[]) {
Serial* SP = new Serial(argv[1]);
char outcomingData[256];
int dataLength = 255;
int sendData;
while (SP->IsConnected()) {
strcpy_s(outcomingData, argv[2]);
sendData = SP->WriteData(outcomingData, dataLength);
printf("%s", outcomingData);
}
return 0;
}
The problem is that the data is sent only when I close the application or delete SP through the destructor. How can I fix this? Is there a workaround or another method that I can add to the class?
It's likely that your data is being cached such that it isn't actually written to your serial device until CloseHandle is called in ~Serial. You can avoid this caching behavior by specifying FILE_FLAG_WRITE_THROUGH in your call to CreateFile, so that the file creation would look something like:
//Try to connect to the given port throuh CreateFile
this->hSerial = CreateFile(portName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
NULL);
If that doesn't work, there's also FILE_FLAG_NO_BUFFERING, but that may impose limitations on the alignment of the data you write to the device.
If you would like to retain the normal buffering behavior, you could add a Serial::Flush method that calls FlushFileBuffers to force Windows to write any data it has cached out to the device:
void Serial::Flush()
{
FileFlushBuffers(this->hSerial);
}
You would then call SP->Flush() after SP->WriteData(...) to ensure that Windows' buffers are flushed to the device. This would allow you to make multiple small writes without having to flush each one, possibly improving performance.
As a side note, I would question your use of new here. It doesn't seem like it's really needed, and SP could just be constructed locally:
Serial SP(argv[1]);
I am pretty new to linux aio (libaio) and am hoping someone here with more experience can help me out.
I have a system that performs high-speed DMA from a PCI device to the system RAM. I then want to write the data to disk using libaio directly from the DMA address space. Currently the memory used for DMA is reserved on boot through the use of the "memmap" kernel boot command.
In my application, the memory is mapped with mmap to a virtual userspace address pointer which I believe I should be able to pass as my buffer to the io_prep_pwrite() call. However, when I do this, the write does not seem to succeed. When the request is reaped with io_getevents the "res" field has error code -22 which prints as "Bad Address".
However, if I do a memcpy from the previously mapped memory location to a new buffer allocated by my application and then use the pointer to this local buffer in the call to io_prep_pwrite the request works just fine and the data is written to disk. The problem is that performing the memcpy creates a bottle-neck for the speed at which I need to stream data to disk and I am unable to keep up with the DMA rate.
I do not understand why I have to copy the memory first for the write request to work. I created a minimal example to illustrate the issue below. The bufBaseLoc is the mapped address and the localBuffer is the address the data is copied to. I do not want to have to perform the following line:
memcpy(localBuffer, bufBaseLoc, WRITE_SIZE);
...or have the localBuffer at all. In the "Prepare IOCB" section I want to use:
io_prep_pwrite(iocb, fid, bufBaseLoc, WRITE_SIZE, 0);
...but it does not work. But the local buffer, which is just a copy, does work.
Does anyone have any insight into why? Any help would be greatly appreciated.
Thanks,
#include <cstdio>
#include <string>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <libaio.h>
#define WRITE_SIZE 0x80000 //4MB buffer
#define DMA_BASE_ADDRESS 0x780000000 //Physical address of memory reserved at boot
//Reaping callback
void writeCallback(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
//Res is number of bytes written by the request. It should match the requested IO size. If negative it is an error code
if(res != (long)iocb->u.c.nbytes)
{
fprintf(stderr, "WRITE_ERROR: %s\n", strerror(-res));
}
else
{
fprintf(stderr, "Success\n");
}
}
int main()
{
//Initialize Kernel AIO
io_context_t ctx = 0;
io_queue_init(256, &ctx);
//Open /dev/mem and map physical address to userspace
int fdmem = open("/dev/mem", O_RDWR);
void *bufBaseLoc = mmap(NULL, WRITE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fdmem, DMA_BASE_ADDRESS);
//Set memory here for test (normally done be DMA)
memset(bufBaseLoc, 1, WRITE_SIZE);
//Initialize Local Memory Buffer (DON’T WANT TO HAVE TO DO THIS)
uint8_t* localBuffer;
posix_memalign((void**)&localBuffer, 4096, WRITE_SIZE);
memset(localBuffer, 1, WRITE_SIZE);
//Open/Allocate file on disk
std::string filename = "tmpData.dat";
int fid = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_DIRECT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
posix_fallocate(fid, 0, WRITE_SIZE);
//Copy from DMA buffer to local process buffer (THIS IS THE COPY I WANT TO AVOID)
memcpy(localBuffer, bufBaseLoc, WRITE_SIZE);
//Prepare IOCB
struct iocb *iocbs[1];
struct iocb *iocb = (struct iocb*) malloc(sizeof (struct iocb));
io_prep_pwrite(iocb, fid, localBuffer, WRITE_SIZE, 0); //<--THIS WORKS (but is not what I want to do)
//io_prep_pwrite(iocb, fid, bufBaseLoc, WRITE_SIZE, 0); //<--THIS DOES NOT WORK (but is what I want to do)
io_set_callback(iocb, writeCallback);
iocbs[0] = iocb;
//Send Request
int res = io_submit(ctx, 1, iocbs);
if (res !=1)
{
fprintf(stderr, "IO_SUBMIT_ERROR: %s\n", strerror(-res));
}
//Reap Request
struct io_event events[1];
size_t ret = io_getevents(ctx, 1, 1, events, 0);
if (ret==1)
{
io_callback_t cb=(io_callback_t)events[0].data;
struct iocb *iocb_e = events[0].obj;
cb(ctx, iocb_e, events[0].res, events[0].res2);
}
}
It could be that your original buffer is not aligned.
Any buffer that libAIO writes to disk needs to be aligned(to around 4K I think). When you allocate your temporary buffer you use posix_memalign, which will allocate it correctly aligned, meaning the write will succeed. If your original reserved buffer is not aligned it could be causing you issues.
If you can try to allocate that initial buffer with an aligned alloc it could solve the issue.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I am new in linux device driver. I wan to write a C/C++ code to perform file transfer from raspberry pi to usb flash drive. I having difficult for the starting point, so i try on libusb for HID device sample code from signal11 and the code works fine for detecting my optical mouse with its device ID. Then i try to obtain usb flash drive vendor id somehow it give me very wired number. Finally i come out with a very silly try out by writing a bash script for cp a file to usb flash drive and activate the script in C++ and it works but i feel it is not a proper way to do it. Then i start with SCSI protocol and i very hard to understand how it works.Any guideline is appreciated.
int scsi_get_serial(int fd, void *buf, size_t buf_len) {
// we shall retrieve page 0x80 as per http://en.wikipedia.org/wiki/SCSI_Inquiry_Command
unsigned char inq_cmd[] = {INQUIRY, 1, 0x80, 0, buf_len, 0};
unsigned char sense[32];
struct sg_io_hdr io_hdr;
int result;
memset(&io_hdr, 0, sizeof (io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmdp = inq_cmd;
io_hdr.cmd_len = sizeof (inq_cmd);
io_hdr.dxferp = buf;
io_hdr.dxfer_len = buf_len;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.sbp = sense;
io_hdr.mx_sb_len = sizeof (sense);
io_hdr.timeout = 5000;
result = ioctl(fd, SG_IO, &io_hdr);
if (result < 0)
return result;
if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK)
return 1;
return 0;
}
int main(int argc, char** argv) {
//char *dev = "/dev/sda";
char *dev = "/dev/sg2";
char scsi_serial[255];
int rc;
int fd;
fd = open(dev, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
perror(dev);
}
memset(scsi_serial, 0, sizeof (scsi_serial));
rc = scsi_get_serial(fd, scsi_serial, 255);
// scsi_serial[3] is the length of the serial number
// scsi_serial[4] is serial number (raw, NOT null terminated)
if (rc < 0) {
printf("FAIL, rc=%d, errno=%d\n", rc, errno);
} else
if (rc == 1) {
printf("FAIL, rc=%d, drive doesn't report serial number\n", rc);
} else {
if (!scsi_serial[3]) {
printf("Failed to retrieve serial for %s\n", dev);
return -1;
}
printf("Serial Number: %.*s\n", (size_t) scsi_serial[3], (char *) & scsi_serial[4]);
}
close(fd);
return (EXIT_SUCCESS);
}
I get this serial number: 00/1F
Then i try write this in test.sh
cp /home/Desktop/stl4.pdf /media/mini_flash
and run system("./test.sh") in C++
The question seems contradictory, at first you say you want to copy a file using a kernel driver, which seems strange to say the least. Then you say you use libusb, which is an userspace library. Then you say that you try to execute a shell script with cp.
Maybe what you want is simply a code snippet that copies a file form an userspace C/C++ program? Try one of these snippets.
In detail, if all you want to do is a C++ equivalent of cp /home/Desktop/stl4.pdf /media/mini_flash, then this is enough:
ifstream in("/home/Desktop/stl4.pdf",ios::binary);
ofstream out("/media/mini_flash/stl4.pdf",ios::binary);
out<<in.rdbuf();
in.close();
out.close();
Using the following code, I'm able to successfully open a raw disk on my machine, but when I get the disk length I get 0 each time...
// Where "Path" is /dev/rdisk1 -- is rdisk1 versus disk1 the proper way to open a raw disk?
Device = open(Path, O_RDWR);
if (Device == -1)
{
throw xException("Error opening device");
}
And getting size with both of these methods returns 0:
struct stat st;
if (stat(Path, &st) == 0)
_Length = st.st_size;
/
_Length = (INT64)lseek(Device, 0, SEEK_END);
lseek(Device, 0, SEEK_SET);
I'm not totally familiar with programming on non-Windows platforms, so please forgive anything that seems odd. My questions here are:
Is this the proper way to open a raw disk under OS X?
What might be causing the disk size to be returned as 0?
The disk in question is an unformatted disk, but for those wanting the info from Disk Utility (with non-important stuff removed):
Name : ST920217 AS Media
Type : Disk
Partition Map Scheme : Unformatted
Disk Identifier : disk1
Media Name : ST920217 AS Media
Media Type : Generic
Writable : Yes
Total Capacity : 20 GB (20,003,880,960 Bytes)
Disk Number : 1
Partition Number : 0
After a little bit of searching through ioctl request codes, I found something that actually works.
#include <sys/disk.h>
#include <sys/ioctl.h>
#include <fcntl.h>
int main()
{
// Open disk
uint32_t dev = open("/dev/disk1", O_RDONLY);
if (dev == -1) {
perror("Failed to open disk");
return -1;
}
uint64_t sector_count = 0;
// Query the number of sectors on the disk
ioctl(dev, DKIOCGETBLOCKCOUNT, §or_count);
uint32_t sector_size = 0;
// Query the size of each sector
ioctl(dev, DKIOCGETBLOCKSIZE, §or_size);
uint64_t disk_size = sector_count * sector_size;
printf("%ld", disk_size);
return 0;
}
Something like that should do the trick. I just copied the code I had into that, so I'm not sure if it would compile alright but it should.