how to use block i/o protocol in my uefi bootloader - freebsd

I am reading FreeBSD uefi bootloader. But there is a part I can't understand about Block I/O Protocol.I quote source code.
status = systab->BootServices->LocateHandle(ByProtocol,
&BlockIoProtocolGUID, NULL, &nparts, handles);
nparts /= sizeof(handles[0]);
for (i = 0; i < nparts; i++) {
status = systab->BootServices->HandleProtocol(handles[i],
&DevicePathGUID, (void **)&devpath);
if (EFI_ERROR(status))
continue;
while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
devpath = NextDevicePathNode(devpath);
status = systab->BootServices->HandleProtocol(handles[i],
&BlockIoProtocolGUID, (void **)&blkio);
if (EFI_ERROR(status))
continue;
if (!blkio->Media->LogicalPartition)
continue;
if (domount(devpath, blkio, 1) >= 0)
break;
}
Why as below the code is searching device path end?
while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
devpath = NextDevicePathNode(devpath);

It looks like the code wants to look at the last node of the device path, so it skips all the nodes until it reaches the one before the end node.

Just want to add a bit to what haggai_e said. DP (device path) is a simple linked list where each node represents sort of a description of a physical or logical device in UEFI environment. And as you probably know each device in UEFI is a handle. So traversing through DP and getting the handle of the particular node gives you a handle to a particular device that DP describes.

Related

SPI linux driver

I am trying to learn how to write a basic SPI driver and below is the probe function that I wrote.
What I am trying to do here is setup the spi device for fram(datasheet) and use the spi_sync_transfer()api description to get the manufacturer's id from the chip.
When I execute this code, I can see the data on the SPI bus using logic analyzer but I am unable to read it using the rx buffer. Am I missing something here? Could someone please help me with this?
static int fram_probe(struct spi_device *spi)
{
int err;
unsigned char ch16[] = {0x9F,0x00,0x00,0x00};// 0x9F => 10011111
unsigned char rx16[] = {0x00,0x00,0x00,0x00};
printk("[FRAM DRIVER] fram_probe called \n");
spi->max_speed_hz = 1000000;
spi->bits_per_word = 8;
spi->mode = (3);
err = spi_setup(spi);
if (err < 0) {
printk("[FRAM DRIVER::fram_probe spi_setup failed!\n");
return err;
}
printk("[FRAM DRIVER] spi_setup ok, cs: %d\n", spi->chip_select);
spi_element[0].tx_buf = ch16;
spi_element[1].rx_buf = rx16;
err = spi_sync_transfer(spi, spi_element, ARRAY_SIZE(spi_element)/2);
printk("rx16=%x %x %x %x\n",rx16[0],rx16[1],rx16[2],rx16[3]);
if (err < 0) {
printk("[FRAM DRIVER]::fram_probe spi_sync_transfer failed!\n");
return err;
}
return 0;
}
spi_element is not declared in this example. You should show that and also how all elements of that are array are filled. But just from the code that's there I see a couple mistakes.
You need to set the len parameter of spi_transfer. You've assigned the TX or RX buffer to ch16 or rx16 but not set the length of the buffer in either case.
You should zero out all the fields not used in the spi_transfer.
If you set the length to four, you would not be sending the proper command according to the datasheet. RDID expects a one byte command after which will follow four bytes of output data. You are writing a four byte command in your first transfer and then reading four bytes of data. The tx_buf in the first transfer should just be one byte.
And finally the number of transfers specified as the last argument to spi_sync_transfer() is incorrect. It should be 2 in this case because you have defined two, spi_element[0] and spi_element[1]. You could use ARRAY_SIZE() if spi_element was declared for the purpose of this message and you want to sent all transfers in the array.
Consider this as a way to better fill in the spi_transfers. It will take care of zeroing out fields that are not used, defines the transfers in a easy to see way, and changing the buffer sizes or the number of transfers is automatically accounted for in remaining code.
const char ch16[] = { 0x8f };
char rx16[4];
struct spi_transfer rdid[] = {
{ .tx_buf = ch16, .len = sizeof(ch16) },
{ .rx_buf = rx16, .len = sizeof(rx16) },
};
spi_transfer(spi, rdid, ARRAY_SIZE(rdid));
Since you have a scope, be sure to check that this operation happens under a single chip select pulse. I have found more than one Linux SPI driver to have a bug that pulses chip select when it should not. In some cases switching from TX to RX (like done above) will trigger a CS pulse. In other cases a CS pulse is generated for every word (8 bits here) of data.
Another thing you should change is use dev_info(&spi->dev, "device version %d", id)' and also dev_err() to print messages. This inserts the device name in a standard way instead of your hard-coded non-standard and inconsistent "[FRAME DRIVER]::" text. And sets the level of the message as appropriate.
Also, consider supporting device tree in your driver to read device properties. Then you can do things like change the SPI bus frequency for this device without rebuilding the kernel driver.

Remove input driver bound to the HID interface

I'm playing with some driver code for a special kind of keyboard. And this keyboard does have special modes. According to the specification those modes could only be enabled by sending and getting feature reports.
I'm using 'hid.c' file and user mode to send HID reports. But both 'hid_read' and 'hid_get_feature_report' failed with error number -1.
I already tried detaching keyboard from kernel drivers using libusb, but when I do that, 'hid_open' fails. I guess this is due to that HID interface already using by 'input' or some driver by the kernel. So I may not need to unbind kernel hidraw driver, instead I should try unbinding the keyboard ('input') driver top of 'hidraw' driver. Am I correct?
And any idea how I could do that? And how to find what are drivers using which drivers and which low level driver bind to which driver?
I found the answer to this myself.
The answer is to dig this project and find it's hid implementation on libusb.
Or you could directly receive the report.
int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
{
int res = -1;
int skipped_report_id = 0;
int report_number = data[0];
if (report_number == 0x0) {
/* Offset the return buffer by 1, so that the report ID
will remain in byte 0. */
data++;
length--;
skipped_report_id = 1;
}
res = libusb_control_transfer(dev->device_handle,
LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN,
0x01/*HID get_report*/,
(3/*HID feature*/ << 8) | report_number,
dev->interface,
(unsigned char *)data, length,
1000/*timeout millis*/);
if (res < 0)
return -1;
if (skipped_report_id)
res++;
return res;
}
I'm sorry I can't post my actual code due to some legal reasons. However the above code is from hidapi implementation.
So even you work with an old kernel , you still have the chance to make your driver working.
This answers to this question too: https://stackoverflow.com/questions/30565999/kernel-version-2-6-32-does-not-support-hidiocgfeature

How to control hard drive motor speed with C on linux?

I've got an old HDD with which I planned to fiddle around a little. First thing I'm trying to do is spinning the motor with different speeds.
Questions are:
Is there a general way to do this or does it depend on the HDD model?
Where do I find a list of commands, that I can send to the HDD controller to control the speed of the motor?
I actually found a function, that apparently spins down the motor, here it is:
/* spin-down a disk */
static void spindown_disk(const char *name)
{
struct sg_io_hdr io_hdr;
unsigned char sense_buf[255];
char dev_name[100];
int fd;
dprintf("spindown: %s\n", name);
/* fabricate SCSI IO request */
memset(&io_hdr, 0x00, sizeof(io_hdr));
io_hdr.interface_id = 'S';
io_hdr.dxfer_direction = SG_DXFER_NONE;
/* SCSI stop unit command */
io_hdr.cmdp = (unsigned char *) "\x1b\x00\x00\x00\x00\x00";
io_hdr.cmd_len = 6;
io_hdr.sbp = sense_buf;
io_hdr.mx_sb_len = (unsigned char) sizeof(sense_buf);
/* open disk device (kernel 2.4 will probably need "sg" names here) */
snprintf(dev_name, sizeof(dev_name), "/dev/%s", name);
if ((fd = open(dev_name, O_RDONLY)) < 0) {
perror(dev_name);
return;
}
/* execute SCSI request */
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
char buf[100];
snprintf(buf, sizeof(buf), "ioctl on %s:", name);
perror(buf);
} else if (io_hdr.masked_status != 0) {
fprintf(stderr, "error: SCSI command failed with status 0x%02x\n",
io_hdr.masked_status);
if (io_hdr.masked_status == CHECK_CONDITION) {
phex(sense_buf, io_hdr.sb_len_wr, "sense buffer:\n");
}
}
close(fd);
}
Though I don't really understand where the actual command is sent to the controller, nor do I know how to control the speed, I don't see any rpm specifications.
You cannot control a harddisk's rotational speed, and that is a good thing. If you could, you would inevitably destroy data.
The heads float in what is commonly called "air bearing".
This is, in easy words, a spring mechanism pressing the head onto the disks's surface with a well-defined force and an air cussion from airflow due to the disk's rotation being in equilibrium at the disk's operational speed. When the disk is shut down, another spring mechanisms quickly pulls the heads out of the way into a kind of "parking position".
If you could run the drive at arbitrary speeds, the heads would scratch on the surface. Not good!
As to where the actual command is being sent in above snippet, it is the ioctl call in the line following /* execute SCSI request */.
If you are interested in playing with your old harddisk (be aware that you'll quite likely break it!), have a look at the hdparm tool and its source code. hdparm lets you tweak dozens of parameters such as power save modes, caching, or acustic management... pretty much everything that disk drives support.
In the tool's source code, you'll find a quite complete list of device commands, too.

Use SATA HDD as Block Device

I'm totally new to the Linux Kernel, so I probably mix things up. But any advice will help me ;)
I have a SATA HDD connected via a PCIe SATA Card and I try to use read and write like on a block device. I also want the data power blackout save on the HDD - not cached. And in the end I have to analyse how much time I loose in each linux stack layer. But one step at a time.
At the moment I try to open the device with *O_DIRECT*. But I don't really understand where I can find the device. It shows up as /dev/sdd and I created one partition /dev/sdd1.
open and read on the partition /dev/sdd1 works. write fails with *O_DIRECT* (But I'm sure I have the right blocksize)
open read and write called on /dev/sdd fails completely.
Is there maybe another file in /dev/ which represents my device on the block layer?
What are my mistakes and wrong assumptions?
This is my current test code
int main() {
int w,r,s;
char buffer[512] = "test string mit 512 byte";
printf("test\n");
// OPEN
int fd = open("/dev/sdd", O_DIRECT | O_RDWR | O_SYNC);
printf("fd = %d\n",fd);
// WRITE
printf("try to write %d byte : %s\n",sizeof(buffer),buffer);
w = write(fd,buffer,sizeof(buffer));
if(w == -1) printf("write failed\n");
else printf("write ok\n");
// RESET BUFFER
memset(buffer,0,sizeof(buffer));
// SEEK
s = lseek(fd,0,SEEK_SET);
if(s == -1) printf("seek failed\n");
else printf("seek ok\n");
// READ
r = read(fd,buffer,sizeof(buffer));
if(r == -1) printf("read failed\n");
else printf("read ok\n");
// PRINT BUFFER
printf("buffer = %s\n",buffer);
return 0;
}
Edit:
I work with the 3.2 Kernel on a power architecture - if this is important.
Thank you very much for your time,
Fabian
Depending on your SDD's block size (could by 512bit or 4K), you can only read/write mulitple of that size.
Also: when using O_DIRECT flag, you need to make sure the buffer is rightly aligned to block boundaries. You cann't ensure that using an ordinary char array, use memalign to allocate aligned memory instead.

How to read from a Linux serial port

I am working on robot which has to control using wireless serial communication. The robot is running on a microcontroller (by burning a .hex file). I want to control it using my Linux (Ubuntu) PC. I am new to serial port programming. I am able to send the data, but I am not able to read data.
A few piece of code which is running over at the microcontroller:
Function to send data:
void TxData(unsigned char tx_data)
{
SBUF = tx_data; // Transmit data that is passed to this function
while(TI == 0) // Wait while data is being transmitted
;
}
I am sending data through an array of characters data_array[i]:
for (i=4; i<=6; i++)
{
TxData(data_array[i]);
RI = 0; // Clear receive interrupt. Must be cleared by the user.
TI = 0; // Clear transmit interrupt. Must be cleared by the user.
}
Now the piece of code from the C program running on Linux...
while (flag == 0) {
int res = read(fd, buf, 255);
buf[res] = 0; /* Set end of string, so we can printf */
printf(":%s:%d\n", buf, res);
if (buf[0] == '\0')
flag = 1;
}
It prints out value of res = 0.
Actually I want to read data character-by-character to perform calculations and take further decision. Is there another way of doing this?
Note: Is there good study material (code) for serial port programming on Linux?
How can I read from the Linux serial port...
This is a good guide: Serial Programming Guide for POSIX Operating Systems
The read call may return with no data and errno set to EAGAIN. You need to check the return value and loop around to read again if you're expecting data to arrive.
First, take a look at /proc/tty/driver/serial to see that everything is set up correctly (i.e., you see the signals you should see). Then, have a look at the manual page for termios(3), you may be interested in the VMIN and VTIME explanation.

Resources